mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 07:08:42 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a3f96b221 |
+35
-21
@@ -157,7 +157,7 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v4-assert
|
||||
- llvm-build-11-linux-v3-assert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -176,7 +176,7 @@ commands:
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v4-assert
|
||||
key: llvm-build-11-linux-v3-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run: make ASSERT=1
|
||||
@@ -225,7 +225,7 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v4-noassert
|
||||
- llvm-build-11-linux-v3-noassert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -244,7 +244,7 @@ commands:
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v4-noassert
|
||||
key: llvm-build-11-linux-v3-noassert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-wasi-libc
|
||||
@@ -286,24 +286,24 @@ commands:
|
||||
- run:
|
||||
name: "Install dependencies"
|
||||
command: |
|
||||
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
|
||||
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.16.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:
|
||||
variant: "macos"
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-macos-v3
|
||||
- llvm-source-11-macos-v2
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-macos-v3
|
||||
key: llvm-source-11-macos-v2
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
@@ -311,7 +311,7 @@ commands:
|
||||
- llvm-project/llvm/include
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-macos-v5
|
||||
- llvm-build-11-macos-v3
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -327,17 +327,17 @@ commands:
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-macos-v5
|
||||
key: llvm-build-11-macos-v3
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-macos-v4
|
||||
- wasi-libc-sysroot-macos-v3
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-macos-v4
|
||||
key: wasi-libc-sysroot-macos-v3
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run:
|
||||
@@ -359,18 +359,30 @@ commands:
|
||||
tinygo version
|
||||
- run: make smoketest AVR=0
|
||||
- save_cache:
|
||||
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-llvm10-go115:
|
||||
test-llvm10-go113:
|
||||
docker:
|
||||
- image: circleci/golang:1.13-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
test-llvm10-go114:
|
||||
docker:
|
||||
- image: circleci/golang:1.14-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
test-llvm11-go115:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
llvm: "11"
|
||||
test-llvm11-go116:
|
||||
docker:
|
||||
- image: circleci/golang:1.16-buster
|
||||
@@ -379,17 +391,17 @@ jobs:
|
||||
llvm: "11"
|
||||
assert-test-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.17-stretch
|
||||
- image: circleci/golang:1.14-stretch
|
||||
steps:
|
||||
- assert-test-linux
|
||||
build-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.17-stretch
|
||||
- image: circleci/golang:1.14-stretch
|
||||
steps:
|
||||
- build-linux
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: "11.1.0" # macOS 10.14
|
||||
xcode: "10.1.0"
|
||||
steps:
|
||||
- build-macos
|
||||
|
||||
@@ -398,7 +410,9 @@ jobs:
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-llvm10-go115
|
||||
- test-llvm10-go113
|
||||
- test-llvm10-go114
|
||||
- test-llvm11-go115
|
||||
- test-llvm11-go116
|
||||
- build-linux
|
||||
- build-macos
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
build/
|
||||
llvm-*/
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
name: CI for tinygo-dev docker container
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, fix-docker-llvm-build ]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to GHCR/Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
ghcr.io/${{ github.repository }}/tinygo-dev
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -16,8 +16,6 @@ src/device/stm32/*.go
|
||||
src/device/stm32/*.s
|
||||
src/device/kendryte/*.go
|
||||
src/device/kendryte/*.s
|
||||
src/device/rp/*.go
|
||||
src/device/rp/*.s
|
||||
vendor
|
||||
llvm-build
|
||||
llvm-project
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ different guide:
|
||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
|
||||
* Go (1.15+)
|
||||
* Go (1.13+)
|
||||
* Standard build tools (gcc/clang)
|
||||
* git
|
||||
* CMake
|
||||
|
||||
-258
@@ -1,261 +1,3 @@
|
||||
0.20.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- add support for Go 1.17
|
||||
- improve Go version detection
|
||||
- add support for the Black Magic Probe (BMP)
|
||||
- add a flag for creating cpu profiles
|
||||
* **compiler**
|
||||
- `builder:` list libraries at the end of the linker command
|
||||
- `builder:` strip debug information at link time instead of at compile time
|
||||
- `builder:` add missing error check for `ioutil.TempFile()`
|
||||
- `builder:` simplify running of jobs
|
||||
- `compiler:` move LLVM math builtin support into the compiler
|
||||
- `compiler:` move math aliases from the runtime to the compiler
|
||||
- `compiler:` add aliases for many hashing packages
|
||||
- `compiler:` add `*ssa.MakeSlice` bounds tests
|
||||
- `compiler:` fix max possible slice
|
||||
- `compiler:` add support for new language features of Go 1.17
|
||||
- `compiler:` fix equally named structs in different scopes
|
||||
- `compiler:` avoid zero-sized alloca in channel operations
|
||||
- `interp:` don't ignore array indices for untyped objects
|
||||
- `interp:` keep reverted package initializers in order
|
||||
- `interp:` fix bug in compiler-time/run-time package initializers
|
||||
- `loader:` fix panic in CGo files with syntax errors
|
||||
- `transform:` improve GC stack slot pass to work around a bug
|
||||
* **standard library**
|
||||
- `crypto/rand`: switch to `arc4random_buf`
|
||||
- `math:` fix `math.Max` and `math.Min`
|
||||
- `math/big`: fix undefined symbols error
|
||||
- `net:` add MAC address implementation
|
||||
- `os:` implement `os.Executable`
|
||||
- `os:` add `SEEK_SET`, `SEEK_CUR`, and `SEEK_END`
|
||||
- `reflect:` add StructField.IsExported method
|
||||
- `runtime:` reset heapptr to heapStart after preinit()
|
||||
- `runtime:` add `subsections_via_symbols` to assembly files on darwin
|
||||
- `testing:` add subset implementation of Benchmark
|
||||
- `testing:` test testing package using `tinygo test`
|
||||
- `testing:` add support for the `-test.v` flag
|
||||
* **targets**
|
||||
- `386:` bump minimum requirement to the Pentium 4
|
||||
- `arm:` switch to Thumb instruction set on ARM
|
||||
- `atsamd:` fix copy-paste error for atsamd21/51 calibTrim block
|
||||
- `baremetal`,`wasm`: support command line params and environment variables
|
||||
- `cortexm:` fix stack overflow because of unaligned stacks
|
||||
- `esp32c3:` add support for the ESP32-C3 from Espressif
|
||||
- `nrf52840:` fix ram size
|
||||
- `nxpmk66f18:` fix a suspicious bitwise operation
|
||||
- `rp2040:` add SPI support
|
||||
- `rp2040:` add I2C support
|
||||
- `rp2040:` add PWM implementation
|
||||
- `rp2040:` add openocd configuration
|
||||
- `stm32:` add support for PortMask* functions for WS2812 support
|
||||
- `unix:` fix time base for time.Now()
|
||||
- `unix:` check for mmap error and act accordingly
|
||||
- `wasm:` override dlmalloc heap implementation from wasi-libc
|
||||
- `wasm:` align heap to 16 bytes
|
||||
- `wasm:` add support for the crypto/rand package
|
||||
* **boards**
|
||||
- add `DefaultUART` to adafruit boards
|
||||
- `arduino-mkrwifi1010:` add board definition for Arduino MKR WiFi 1010
|
||||
- `arduino-mkrwifi1010:` fix pin definition of `NINA_RESETN`
|
||||
- `feather-nrf52:` fix pin definition of uart
|
||||
- `feather-rp2040:` add pin name definition
|
||||
- `gameboy-advance:` fix ROM header
|
||||
- `mdbt50qrx-uf2:` add Raytac MDBT50Q-RX Dongle with TinyUF2
|
||||
- `nano-rp2040:` define `NINA_SPI` and fix wifinina pins
|
||||
- `teensy40:` enable hardware UART reconfiguration, fix receive watermark interrupt
|
||||
|
||||
|
||||
0.19.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- don't consider compile-only tests as failing
|
||||
- add -test flag for `tinygo list`
|
||||
- escape commands while printing them with the -x flag
|
||||
- make flash-command portable and safer to use
|
||||
- use `extended-remote` instead of `remote` in GDB
|
||||
- detect specific serial port IDs based on USB vid/pid
|
||||
- add a flag to the command line to select the serial implementation
|
||||
* **compiler**
|
||||
- `cgo`: improve constant parser
|
||||
- `compiler`: support chained interrupt handlers
|
||||
- `compiler`: add support for running a builtin in a goroutine
|
||||
- `compiler`: do not emit nil checks for loading closure variables
|
||||
- `compiler`: skip context parameter when starting regular goroutine
|
||||
- `compiler`: refactor method names
|
||||
- `compiler`: add function and global section pragmas
|
||||
- `compiler`: implement `syscall.rawSyscallNoError` in inline assembly
|
||||
- `interp`: ignore inline assembly in markExternal
|
||||
- `interp`: fix a bug in pointer cast workaround
|
||||
- `loader`: fix testing a main package
|
||||
* **standard library**
|
||||
- `crypto/rand`: replace this package with a TinyGo version
|
||||
- `machine`: make USBCDC global a pointer
|
||||
- `machine`: make UART objects pointer receivers
|
||||
- `machine`: define Serial as the default output
|
||||
- `net`: add initial support for net.IP
|
||||
- `net`: add more net compatibility
|
||||
- `os`: add stub for os.ReadDir
|
||||
- `os`: add FileMode constants from Go 1.16
|
||||
- `os`: add stubs required for net/http
|
||||
- `os`: implement process related functions
|
||||
- `reflect`: implement AppendSlice
|
||||
- `reflect`: add stubs required for net/http
|
||||
- `runtime`: make task.Data a 64-bit integer to avoid overflow
|
||||
- `runtime`: expose memory stats
|
||||
- `sync`: implement NewCond
|
||||
- `syscall`: fix int type in libc version
|
||||
* **targets**
|
||||
- `cortexm`: do not disable interrupts on abort
|
||||
- `cortexm`: bump default stack size to 2048 bytes
|
||||
- `nrf`: avoid heap allocation in waitForEvent
|
||||
- `nrf`: don't trigger a heap allocation in SPI.Transfer
|
||||
- `nrf52840`: add support for flashing with the BOSSA tool
|
||||
- `rp2040`: add support for GPIO input
|
||||
- `rp2040`: add basic support for ADC
|
||||
- `rp2040`: gpio and adc pin definitions
|
||||
- `rp2040`: implement UART
|
||||
- `rp2040`: patch elf to checksum 2nd stage boot
|
||||
- `stm32`: add PWM for most chips
|
||||
- `stm32`: add support for pin interrupts
|
||||
- `stm32f103`: add support for PinInputPullup / PinInputPulldown
|
||||
- `wasi`: remove wasm build tag
|
||||
* **boards**
|
||||
- `feather-rp2040`: add support for this board
|
||||
- `feather-nrf52840-sense`: add board definition for this board
|
||||
- `pca10059`: support flashing from Windows
|
||||
- `nano-rp2040`: add this board
|
||||
- `nano-33-ble`: add support for this board
|
||||
- `pico`: add the Raspberry Pi Pico board with the new RP2040 chip
|
||||
- `qtpy`: add pin for neopixels
|
||||
- all: add definition for ws2812 for supported boards
|
||||
|
||||
|
||||
0.18.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- drop support for Go 1.11 and 1.12
|
||||
- throw an error when no target is specified on Windows
|
||||
- improve error messages in `getDefaultPort()`, support for multiple ports
|
||||
- remove `-cflags` and `-ldflags` flags
|
||||
- implement `-ldflags="-X ..."`
|
||||
- add `-print-allocs` flag that lets you print all heap allocations
|
||||
- openocd commands in tinygo command line
|
||||
- add `-llvm-features` parameter
|
||||
- match `go test` output
|
||||
- discover USB ports only, this will ignore f.ex. bluetooth
|
||||
- use physicmal path instead of cached GOROOT in function getGoroot
|
||||
- add goroot for snap installs
|
||||
* **compiler**
|
||||
- `builder`: add support for `-opt=0`
|
||||
- `builder`, `compiler`: compile and cache packages in parallel
|
||||
- `builder`: run interp per package
|
||||
- `builder`: cache C and assembly file outputs
|
||||
- `builder`: add support for `-x` flag to print commands
|
||||
- `builder`: add optsize attribute while building the package
|
||||
- `builder`: run function passes per package
|
||||
- `builder`: hard code Clang compiler
|
||||
- `compiler`: do not use `llvm.GlobalContext()`
|
||||
- `compiler`: remove SimpleDCE pass
|
||||
- `compiler`: do not emit nil checks for `*ssa.Alloc` instructions
|
||||
- `compiler`: merge `runtime.typecodeID` and runtime.typeInInterface
|
||||
- `compiler`: do not check for impossible type asserts
|
||||
- `compiler`: fix use of global context: `llvm.Int32Type()`
|
||||
- `compiler`: add interface IR test
|
||||
- `compiler`: fix lack of method name in interface matching
|
||||
- `compiler`: fix "fragment covers entire variable" bug
|
||||
- `compiler`: optimize string literals and globals
|
||||
- `compiler`: decouple func lowering from interface type codes
|
||||
- `compiler`: add function attributes to some runtime calls
|
||||
- `compiler`: improve position information in error messages
|
||||
- `cgo`: add support for CFLAGS in .c files
|
||||
- `interp`: support GEP on fixed (MMIO) addresses
|
||||
- `interp`: handle `(reflect.Type).Elem()`
|
||||
- `interp`: add support for runtime.interfaceMethod
|
||||
- `interp`: make toLLVMValue return an error instead of panicking
|
||||
- `interp`: add support for switch statement
|
||||
- `interp`: fix phi instruction
|
||||
- `interp`: remove map support
|
||||
- `interp`: support extractvalue/insertvalue with multiple operands
|
||||
- `transform`: optimize string comparisons against ""
|
||||
- `transform`: optimize `reflect.Type` `Implements()` method
|
||||
- `transform`: fix bug in interface lowering when signatures are renamed
|
||||
- `transform`: don't rely on struct name of `runtime.typecodeID`
|
||||
- `transform`: use IPSCCP pass instead of the constant propagation pass
|
||||
- `transform`: fix func lowering assertion failure
|
||||
- `transform`: do not lower zero-sized alloc to alloca
|
||||
- `transform`: split interface and reflect lowering
|
||||
* **standard library**
|
||||
- `runtime`: add dummy debug package
|
||||
- `machine`: fix data shift/mask in newUSBSetup
|
||||
- `machine`: make `machine.I2C0` and similar objects pointers
|
||||
- `machine`: unify usbcdc code
|
||||
- `machine`: refactor PWM support
|
||||
- `machine`: avoid heap allocations in USB code
|
||||
- `reflect`: let `reflect.Type` be of interface type
|
||||
- `reflect`: implement a number of stub functions
|
||||
- `reflect`: check for access in the `Interface` method call
|
||||
- `reflect`: fix `AssignableTo` and `Implements` methods
|
||||
- `reflect`: implement `Value.CanAddr`
|
||||
- `reflect`: implement `Sizeof` and `Alignof` for func values
|
||||
- `reflect`: implement `New` function
|
||||
- `runtime`: implement command line arguments in hosted environments
|
||||
- `runtime`: implement environment variables for Linux
|
||||
- `runtime`: improve timers on nrf, and samd chips
|
||||
* **targets**
|
||||
- all: use -Qunused-arguments only for assembly files
|
||||
- `atmega1280`: add PWM support
|
||||
- `attiny`: remove dummy UART
|
||||
- `atsamd21`: improve SPI
|
||||
- `atsamd51`: fix PWM support in atsamd51p20
|
||||
- `atsamd5x`: improve SPI
|
||||
- `atsamd51`, `atsame5x`: unify samd51 and same5x
|
||||
- `atsamd51`, `atsamd21`: fix `ADC.Get()` value at 8bit and 10bit
|
||||
- `atsame5x`: add support for CAN
|
||||
- `avr`: remove I2C stubs from attiny support
|
||||
- `cortexm`: check for `arm-none-eabi-gdb` and `gdb-multiarch` commands
|
||||
- `cortexm`: add `__isr_vector` symbol
|
||||
- `cortexm`: disable FPU on Cortex-M4
|
||||
- `cortexm`: clean up Cortex-M target files
|
||||
- `fe310`: fix SPI read
|
||||
- `gameboy-advance`: Fix RGBA color interpretation
|
||||
- `nrf52833`: add PWM support
|
||||
- `stm32l0`: use unified UART logic
|
||||
- `stm32`: move f103 (bluepill) to common i2c code
|
||||
- `stm32`: separate altfunc selection for UART Tx/Rx
|
||||
- `stm32`: i2c implementation for F7, L5 and L4 MCUs
|
||||
- `stm32`: make SPI CLK fast to fix data issue
|
||||
- `stm32`: support SPI on L4 series
|
||||
- `unix`: avoid possible heap allocation with `-opt=0`
|
||||
- `unix`: use conservative GC by default
|
||||
- `unix`: use the tasks scheduler instead of coroutines
|
||||
- `wasi`: upgrade WASI version to wasi_snapshot_preview1
|
||||
- `wasi`: darwin: support basic file io based on libc
|
||||
- `wasm`: only export explicitly exported functions
|
||||
- `wasm`: use WASI ABI for exit function
|
||||
- `wasm`: scan globals conservatively
|
||||
* **boards**
|
||||
- `arduino-mega1280`: add support for the Arduino Mega 1280
|
||||
- `arduino-nano-new`: Add Arduino Nano w/ New Bootloader target
|
||||
- `atsame54-xpro`: add initial support this board
|
||||
- `feather-m4-can`: add initial support for this board
|
||||
- `grandcentral-m4`: add board support for Adafruit Grand Central M4 (SAMD51)
|
||||
- `lgt92`: update to new UART structure
|
||||
- `microbit`: remove LED constant
|
||||
- `microbit-v2`: add support for S113 SoftDevice
|
||||
- `nucleol432`: add support for this board
|
||||
- `nucleo-l031k6`: add this board
|
||||
- `pca10059`: initial support for this board
|
||||
- `qtpy`: fix msd-volume-name
|
||||
- `qtpy`: fix i2c setting
|
||||
- `teensy40`: move txBuffer allocation to UART declaration
|
||||
- `teensy40`: add UART0 as alias for UART1
|
||||
|
||||
|
||||
0.17.0
|
||||
|
||||
---
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
# TinyGo base stage installs the most recent Go 1.17.x, LLVM 11 and the TinyGo compiler itself.
|
||||
FROM golang:1.17 AS tinygo-base
|
||||
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 11 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/bullseye/ llvm-toolchain-bullseye-11 main" >> /etc/apt/sources.list && \
|
||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ else
|
||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||
endif
|
||||
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine frontendopenmp instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||
|
||||
@@ -71,7 +71,7 @@ else
|
||||
endif
|
||||
|
||||
# Libraries that should be linked in for the statically linked Clang.
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIB_NAMES = clangAnalysis clangARCMigrate clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangStaticAnalyzerCheckers clangStaticAnalyzerCore clangStaticAnalyzerFrontend clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||
|
||||
# Libraries that should be linked in for the statically linked LLD.
|
||||
@@ -100,14 +100,14 @@ endif
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
||||
fmt:
|
||||
@gofmt -l -w $(FMT_PATHS)
|
||||
fmt-check:
|
||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||
|
||||
|
||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp
|
||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp
|
||||
ifneq ($(STM32), 0)
|
||||
gen-device: gen-device-stm32
|
||||
endif
|
||||
@@ -124,7 +124,6 @@ build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||
|
||||
gen-device-esp: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif -interrupts=software lib/cmsis-svd/data/Espressif/ src/device/esp/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/esp
|
||||
|
||||
gen-device-nrf: build/gen-device-svd
|
||||
@@ -151,9 +150,6 @@ gen-device-stm32: build/gen-device-svd
|
||||
./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
|
||||
|
||||
gen-device-rp: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/RaspberryPi lib/cmsis-svd/data/RaspberryPi/ src/device/rp/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/rp
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
@@ -163,7 +159,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
|
||||
|
||||
# Build LLVM.
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
@@ -186,33 +182,25 @@ tinygo:
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
TEST_PACKAGES = \
|
||||
container/heap \
|
||||
container/list \
|
||||
container/ring \
|
||||
crypto/des \
|
||||
crypto/md5 \
|
||||
crypto/sha1 \
|
||||
crypto/sha256 \
|
||||
crypto/sha512 \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
encoding/base32 \
|
||||
encoding/hex \
|
||||
hash/adler32 \
|
||||
hash/fnv \
|
||||
hash/crc64 \
|
||||
math \
|
||||
math/cmplx \
|
||||
testing \
|
||||
text/scanner \
|
||||
unicode/utf8 \
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||
# implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(TINYGO) test $(TEST_PACKAGES)
|
||||
$(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:
|
||||
@@ -236,8 +224,6 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/memstats
|
||||
@$(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=pca10040 examples/pininterrupt
|
||||
@@ -342,11 +328,9 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840-sense examples/blinky1
|
||||
@$(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/serial
|
||||
$(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
|
||||
@@ -356,24 +340,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/can
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/caninterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mkrwifi1010 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -404,8 +372,6 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(AVR), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||
@@ -433,8 +399,6 @@ ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(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
|
||||
@@ -448,15 +412,10 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(OS),Windows_NT)
|
||||
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
||||
endif
|
||||
|
||||
|
||||
wasmtest:
|
||||
|
||||
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 70 microcontroller boards are currently supported:
|
||||
The following 57 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
@@ -52,10 +52,7 @@ The following 70 microcontroller boards are currently supported:
|
||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
|
||||
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||
* [Adafruit Feather nRF52840 Sense](https://www.adafruit.com/product/4516)
|
||||
* [Adafruit Feather RP2040](https://www.adafruit.com/product/4884)
|
||||
* [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382)
|
||||
* [Adafruit Grand Central M4](https://www.adafruit.com/product/4064)
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
||||
@@ -66,15 +63,10 @@ The following 70 microcontroller boards are currently supported:
|
||||
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||
* [Arduino MKR WiFi 1010](https://store.arduino.cc/usa/mkr-wifi-1010)
|
||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
||||
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
||||
* [Arduino Nano 33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Nano RP2040 Connect](https://store.arduino.cc/nano-rp2040-connect)
|
||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
@@ -92,7 +84,6 @@ The following 70 microcontroller boards are currently supported:
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
* [Nordic Semiconductor pca10059](https://www.nordicsemi.com/Software-and-tools/Development-Kits/nRF52840-Dongle)
|
||||
* [Particle Argon](https://docs.particle.io/datasheets/wi-fi/argon-datasheet/)
|
||||
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
|
||||
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
||||
@@ -101,16 +92,12 @@ The following 70 microcontroller boards are currently supported:
|
||||
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [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 "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
||||
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
|
||||
* [ST Micro "Nucleo" L432KC](https://www.st.com/ja/evaluation-tools/nucleo-l432kc.html)
|
||||
* [ST Micro "Nucleo" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html)
|
||||
* [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)
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
inputs:
|
||||
version: '1.17'
|
||||
version: '1.16'
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
- task: Cache@2
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- task: CacheBeta@0
|
||||
displayName: Cache LLVM build
|
||||
inputs:
|
||||
key: llvm-build-11-windows-v5
|
||||
key: llvm-build-11-windows-v4
|
||||
path: llvm-build
|
||||
- task: Bash@3
|
||||
displayName: Build LLVM
|
||||
|
||||
+92
-156
@@ -12,9 +12,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"hash/crc32"
|
||||
"io/ioutil"
|
||||
"math/bits"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -41,11 +39,6 @@ type BuildResult struct {
|
||||
// The directory of the main package. This is useful for testing as the test
|
||||
// binary must be run in the directory of the tested package.
|
||||
MainDir string
|
||||
|
||||
// ImportPath is the import path of the main package. This is useful for
|
||||
// correctly printing test results: the import path isn't always the same as
|
||||
// the path listed on the command line.
|
||||
ImportPath string
|
||||
}
|
||||
|
||||
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
||||
@@ -100,8 +93,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
DefaultStackSize: config.Target.DefaultStackSize,
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
Debug: true,
|
||||
LLVMFeatures: config.LLVMFeatures(),
|
||||
Debug: config.Debug(),
|
||||
}
|
||||
|
||||
// Load the target machine, which is the LLVM object that contains all
|
||||
@@ -124,6 +116,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return err
|
||||
}
|
||||
|
||||
// The slice of jobs that orchestrates most of the build.
|
||||
// This is somewhat like an in-memory Makefile with each job being a
|
||||
// Makefile target.
|
||||
var jobs []*compileJob
|
||||
|
||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||
// program so it's pretty fast and doesn't need to be parallelized.
|
||||
program := lprogram.LoadSSA()
|
||||
@@ -303,9 +300,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(f.Name(), bitcodePath)
|
||||
|
||||
// Rename may fail if another process is trying to write to
|
||||
// the same file. However, in this case, the failure is
|
||||
// acceptable because the result of the other process can be
|
||||
// used.
|
||||
os.Rename(f.Name(), bitcodePath)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
packageJobs = append(packageJobs, job)
|
||||
}
|
||||
|
||||
@@ -396,13 +400,14 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return nil
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, programJob)
|
||||
|
||||
// Check whether we only need to create an object file.
|
||||
// If so, we don't need to link anything and will be finished quickly.
|
||||
outext := filepath.Ext(outpath)
|
||||
if outext == ".o" || outext == ".bc" || outext == ".ll" {
|
||||
// Run jobs to produce the LLVM module.
|
||||
err := runJobs(programJob)
|
||||
err := runJobs(jobs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -443,6 +448,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, outputObjectFileJob)
|
||||
|
||||
// Prepare link command.
|
||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||
@@ -457,13 +463,38 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
jobs = append(jobs, job.dependencies...)
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
|
||||
// Add libc dependency if needed.
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
switch config.Target.Libc {
|
||||
case "picolibc":
|
||||
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The library needs to be compiled (cache miss).
|
||||
jobs = append(jobs, job.dependencies...)
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
case "wasi-libc":
|
||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||
}
|
||||
ldflags = append(ldflags, path)
|
||||
case "":
|
||||
// no library specified, so nothing to do
|
||||
default:
|
||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||
}
|
||||
|
||||
// Add jobs to compile extra files. These files are in C or assembly and
|
||||
// contain things like the interrupt vector table and low level operations
|
||||
// such as stack switching.
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
for _, path := range config.ExtraFiles() {
|
||||
abspath := filepath.Join(root, path)
|
||||
job := &compileJob{
|
||||
@@ -474,6 +505,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return err
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
|
||||
@@ -492,6 +524,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return err
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
}
|
||||
@@ -502,63 +535,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||
}
|
||||
|
||||
// Add libc dependency if needed.
|
||||
switch config.Target.Libc {
|
||||
case "picolibc":
|
||||
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
case "wasi-libc":
|
||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||
}
|
||||
job := dummyCompileJob(path)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
case "":
|
||||
// no library specified, so nothing to do
|
||||
default:
|
||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||
}
|
||||
|
||||
// Strip debug information with -no-debug.
|
||||
if !config.Debug() {
|
||||
for _, tag := range config.BuildTags() {
|
||||
if tag == "baremetal" {
|
||||
// Don't use -no-debug on baremetal targets. It makes no sense:
|
||||
// the debug information isn't flashed to the device anyway.
|
||||
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||
}
|
||||
}
|
||||
if config.Target.Linker == "wasm-ld" {
|
||||
// Don't just strip debug information, also compress relocations
|
||||
// while we're at it. Relocations can only be compressed when debug
|
||||
// information is stripped.
|
||||
ldflags = append(ldflags, "--strip-debug", "--compress-relocations")
|
||||
} else {
|
||||
switch config.GOOS() {
|
||||
case "linux":
|
||||
// Either real linux or an embedded system (like AVR) that
|
||||
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||
// case.
|
||||
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||
case "darwin":
|
||||
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||
// information. Apple expects you to use the strip command
|
||||
// instead.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||
default:
|
||||
// Other OSes may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a linker job, which links all object files together and does some
|
||||
// extra stuff that can only be done after linking.
|
||||
linkJob := &compileJob{
|
||||
jobs = append(jobs, &compileJob{
|
||||
description: "link",
|
||||
dependencies: linkerDependencies,
|
||||
run: func(job *compileJob) error {
|
||||
@@ -568,8 +547,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
}
|
||||
ldflags = append(ldflags, dependency.result)
|
||||
}
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||
if config.Options.PrintCommands {
|
||||
fmt.Printf("%s %s\n", config.Target.Linker, strings.Join(ldflags, " "))
|
||||
}
|
||||
err = link(config.Target.Linker, ldflags...)
|
||||
if err != nil {
|
||||
@@ -587,8 +566,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Apply ELF patches
|
||||
if config.AutomaticStackSize() {
|
||||
// Modify the .tinygo_stacksizes section that contains a stack size
|
||||
// for each goroutine.
|
||||
@@ -597,13 +574,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return fmt.Errorf("could not modify stack sizes: %w", err)
|
||||
}
|
||||
}
|
||||
if config.RP2040BootPatch() {
|
||||
// Patch the second stage bootloader CRC into the .boot2 section
|
||||
err = patchRP2040BootCRC(executable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
||||
sizes, err := loadProgramSize(executable)
|
||||
@@ -631,12 +601,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// Run all jobs to compile and link the program.
|
||||
// Do this now (instead of after elf-to-hex and similar conversions) as it
|
||||
// is simpler and cannot be parallelized.
|
||||
err = runJobs(linkJob)
|
||||
err = runJobs(jobs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -661,7 +631,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "esp32", "esp32c3", "esp8266":
|
||||
case "esp32", "esp8266":
|
||||
// Special format for the ESP family of chips (parsed by the ROM
|
||||
// bootloader).
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
@@ -669,25 +639,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "nrf-dfu":
|
||||
// special format for nrfutil for Nordic chips
|
||||
tmphexpath := filepath.Join(dir, "main.hex")
|
||||
err := objcopy(executable, tmphexpath, "hex")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err = makeDFUFirmwareImage(config.Options, tmphexpath, tmppath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||
}
|
||||
return action(BuildResult{
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
ImportPath: lprogram.MainPkg().ImportPath,
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -950,7 +907,30 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
// stack size information. Before this modification, all stack sizes in the
|
||||
// section assume the default stack size (which is relatively big).
|
||||
func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map[string]functionStackSize) error {
|
||||
data, fileHeader, err := getElfSectionData(executable, ".tinygo_stacksizes")
|
||||
fp, err := os.OpenFile(executable, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
elfFile, err := elf.NewFile(fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
section := elfFile.Section(".tinygo_stacksizes")
|
||||
if section == nil {
|
||||
return errors.New("could not find .tinygo_stacksizes section")
|
||||
}
|
||||
|
||||
if section.Size != section.FileSize {
|
||||
// Sanity check.
|
||||
return fmt.Errorf("expected .tinygo_stacksizes to have identical size and file size, got %d and %d", section.Size, section.FileSize)
|
||||
}
|
||||
|
||||
// Read all goroutine stack sizes.
|
||||
data := make([]byte, section.Size)
|
||||
_, err = fp.ReadAt(data, int64(section.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -972,19 +952,15 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
||||
if fn.stackSizeType == stacksize.Bounded {
|
||||
stackSize := uint32(fn.stackSize)
|
||||
|
||||
// Add stack size used by interrupts.
|
||||
switch fileHeader.Machine {
|
||||
case elf.EM_ARM:
|
||||
if stackSize%8 != 0 {
|
||||
// If the stack isn't a multiple of 8, it means the leaf
|
||||
// function with the biggest stack depth doesn't have an aligned
|
||||
// stack. If the STKALIGN flag is set (which it is by default)
|
||||
// the interrupt controller will forcibly align the stack before
|
||||
// storing in-use registers. This will thus overwrite one word
|
||||
// past the end of the stack (off-by-one).
|
||||
stackSize += 4
|
||||
}
|
||||
// Adding 4 for the stack canary. Even though the size may be
|
||||
// automatically determined, stack overflow checking is still
|
||||
// important as the stack size cannot be determined for all
|
||||
// goroutines.
|
||||
stackSize += 4
|
||||
|
||||
// Add stack size used by interrupts.
|
||||
switch elfFile.Machine {
|
||||
case elf.EM_ARM:
|
||||
// On Cortex-M (assumed here), this stack size is 8 words or 32
|
||||
// bytes. This is only to store the registers that the interrupt
|
||||
// may modify, the interrupt will switch to the interrupt stack
|
||||
@@ -992,14 +968,6 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
||||
// Some background:
|
||||
// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
|
||||
stackSize += 32
|
||||
|
||||
// Adding 4 for the stack canary, and another 4 to keep the
|
||||
// stack aligned. Even though the size may be automatically
|
||||
// determined, stack overflow checking is still important as the
|
||||
// stack size cannot be determined for all goroutines.
|
||||
stackSize += 8
|
||||
default:
|
||||
return fmt.Errorf("unknown architecture: %s", fileHeader.Machine.String())
|
||||
}
|
||||
|
||||
// Finally write the stack size to the binary.
|
||||
@@ -1007,7 +975,13 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
||||
}
|
||||
}
|
||||
|
||||
return replaceElfSection(executable, ".tinygo_stacksizes", data)
|
||||
// Write back the modified stack sizes.
|
||||
_, err = fp.WriteAt(data, int64(section.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// printStacks prints the maximum stack depth for functions that are started as
|
||||
@@ -1039,41 +1013,3 @@ func printStacks(calculatedStacks []string, stackSizes map[string]functionStackS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RP2040 second stage bootloader CRC32 calculation
|
||||
//
|
||||
// Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
|
||||
// Section: 2.8.1.3.1. Checksum
|
||||
func patchRP2040BootCRC(executable string) error {
|
||||
bytes, _, err := getElfSectionData(executable, ".boot2")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(bytes) != 256 {
|
||||
return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes")
|
||||
}
|
||||
|
||||
// From the 'official' RP2040 checksum script:
|
||||
//
|
||||
// Our bootrom CRC32 is slightly bass-ackward but it's
|
||||
// best to work around for now (FIXME)
|
||||
// 100% worth it to save two Thumb instructions
|
||||
revBytes := make([]byte, len(bytes))
|
||||
for i := range bytes {
|
||||
revBytes[i] = bits.Reverse8(bytes[i])
|
||||
}
|
||||
|
||||
// crc32.Update does an initial negate and negates the
|
||||
// result, so to meet RP2040 spec, pass 0x0 as initial
|
||||
// hash and negate returned value.
|
||||
//
|
||||
// Note: checksum is over 252 bytes (256 - 4)
|
||||
hash := bits.Reverse32(crc32.Update(0x0, crc32.IEEETable, revBytes[:252]) ^ 0xFFFFFFFF)
|
||||
|
||||
// Write the CRC to the end of the bootloader.
|
||||
binary.LittleEndian.PutUint32(bytes[252:], hash)
|
||||
|
||||
// Update the .boot2 section to included the CRC
|
||||
return replaceElfSection(executable, ".boot2", bytes)
|
||||
}
|
||||
|
||||
+3
-7
@@ -56,7 +56,7 @@ import (
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// written on each new compilation (even when it seems unnecessary). However, it
|
||||
// could in rare cases lead to a stale file fetched from the cache.
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands bool) (string, error) {
|
||||
// Hash input file.
|
||||
fileHash, err := hashFile(abspath)
|
||||
if err != nil {
|
||||
@@ -128,8 +128,8 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
// flags (for the assembler) is a compiler error.
|
||||
flags = append(flags, "-Qunused-arguments")
|
||||
}
|
||||
if printCommands != nil {
|
||||
printCommands("clang", flags...)
|
||||
if printCommands {
|
||||
fmt.Printf("clang %s\n", strings.Join(flags, " "))
|
||||
}
|
||||
err = runCCompiler(flags...)
|
||||
if err != nil {
|
||||
@@ -155,10 +155,6 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
|
||||
// Write dependencies file.
|
||||
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
||||
if err != nil {
|
||||
panic(err) // shouldn't happen
|
||||
|
||||
+2
-12
@@ -17,28 +17,18 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.OpenOCDCommands != nil {
|
||||
// Override the OpenOCDCommands from the target spec if specified on
|
||||
// the command-line
|
||||
spec.OpenOCDCommands = options.OpenOCDCommands
|
||||
}
|
||||
|
||||
goroot := goenv.Get("GOROOT")
|
||||
if goroot == "" {
|
||||
return nil, errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
|
||||
major, minor, err := goenv.GetGorootVersion(goroot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
if major != 1 || minor < 15 || minor > 17 {
|
||||
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
||||
if major != 1 || minor < 13 || minor > 16 {
|
||||
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
return &compileopts.Config{
|
||||
Options: options,
|
||||
Target: spec,
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func getElfSectionData(executable string, sectionName string) ([]byte, elf.FileHeader, error) {
|
||||
elfFile, err := elf.Open(executable)
|
||||
if err != nil {
|
||||
return nil, elf.FileHeader{}, err
|
||||
}
|
||||
defer elfFile.Close()
|
||||
|
||||
section := elfFile.Section(sectionName)
|
||||
if section == nil {
|
||||
return nil, elf.FileHeader{}, fmt.Errorf("could not find %s section", sectionName)
|
||||
}
|
||||
|
||||
data, err := section.Data()
|
||||
|
||||
return data, elfFile.FileHeader, err
|
||||
}
|
||||
|
||||
func replaceElfSection(executable string, sectionName string, data []byte) error {
|
||||
fp, err := os.OpenFile(executable, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
elfFile, err := elf.Open(executable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer elfFile.Close()
|
||||
|
||||
section := elfFile.Section(sectionName)
|
||||
if section == nil {
|
||||
return fmt.Errorf("could not find %s section", sectionName)
|
||||
}
|
||||
|
||||
// Implicitly check for compressed sections
|
||||
if section.Size != section.FileSize {
|
||||
return fmt.Errorf("expected section %s to have identical size and file size, got %d and %d", sectionName, section.Size, section.FileSize)
|
||||
}
|
||||
|
||||
// Only permit complete replacement of section
|
||||
if section.Size != uint64(len(data)) {
|
||||
return fmt.Errorf("expected section %s to have size %d, was actually %d", sectionName, len(data), section.Size)
|
||||
}
|
||||
|
||||
// Write the replacement section data
|
||||
_, err = fp.WriteAt(data, int64(section.Offset))
|
||||
return err
|
||||
}
|
||||
+6
-19
@@ -78,21 +78,11 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
// An added benefit is that we don't need to check for errors all the time.
|
||||
outf := &bytes.Buffer{}
|
||||
|
||||
// Chip IDs. Source:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
}[format]
|
||||
|
||||
// Image header.
|
||||
switch format {
|
||||
case "esp32", "esp32c3":
|
||||
case "esp32":
|
||||
// Header format:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||
// Note: not adding a SHA256 hash as the binary is modified by
|
||||
// esptool.py while flashing and therefore the hash won't be valid
|
||||
// anymore.
|
||||
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
|
||||
binary.Write(outf, binary.LittleEndian, struct {
|
||||
magic uint8
|
||||
segment_count uint8
|
||||
@@ -101,18 +91,15 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
entry_addr uint32
|
||||
wp_pin uint8
|
||||
spi_pin_drv [3]uint8
|
||||
chip_id uint16
|
||||
min_chip_rev uint8
|
||||
reserved [8]uint8
|
||||
reserved [11]uint8
|
||||
hash_appended bool
|
||||
}{
|
||||
magic: 0xE9,
|
||||
segment_count: byte(len(segments)),
|
||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||
spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB
|
||||
spi_mode: 0, // irrelevant, replaced by esptool when flashing
|
||||
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
|
||||
entry_addr: uint32(inf.Entry),
|
||||
wp_pin: 0xEE, // disable WP pin
|
||||
chip_id: chip_id,
|
||||
hash_appended: true, // add a SHA256 hash
|
||||
})
|
||||
case "esp8266":
|
||||
@@ -155,7 +142,7 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||
outf.WriteByte(checksum)
|
||||
|
||||
if format != "esp8266" {
|
||||
if format == "esp32" {
|
||||
// SHA256 hash (to protect against image corruption, not for security).
|
||||
hash := sha256.Sum256(outf.Bytes())
|
||||
outf.Write(hash[:])
|
||||
|
||||
+6
-23
@@ -65,29 +65,12 @@ func (job *compileJob) readyToRun() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// runJobs runs the indicated job and all its dependencies. For every job, all
|
||||
// the dependencies are run first. It returns the error of the first job that
|
||||
// fails.
|
||||
// It runs all jobs in the order of the dependencies slice, depth-first.
|
||||
// Therefore, if some jobs are preferred to run before others, they should be
|
||||
// ordered as such in the job dependencies.
|
||||
func runJobs(job *compileJob) error {
|
||||
// Create a slice of jobs to run, where all dependencies are run in order.
|
||||
jobs := []*compileJob{}
|
||||
addedJobs := map[*compileJob]struct{}{}
|
||||
var addJobs func(*compileJob)
|
||||
addJobs = func(job *compileJob) {
|
||||
if _, ok := addedJobs[job]; ok {
|
||||
return
|
||||
}
|
||||
for _, dep := range job.dependencies {
|
||||
addJobs(dep)
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
addedJobs[job] = struct{}{}
|
||||
}
|
||||
addJobs(job)
|
||||
|
||||
// runJobs runs all the jobs indicated in the jobs slice and returns the error
|
||||
// of the first job that fails to run.
|
||||
// It runs all jobs in the order of the slice, as long as all dependencies have
|
||||
// already run. Therefore, if some jobs are preferred to run before others, they
|
||||
// should be ordered as such in this slice.
|
||||
func runJobs(jobs []*compileJob) error {
|
||||
// Create channels to communicate with the workers.
|
||||
doneChan := make(chan *compileJob)
|
||||
workerChan := make(chan *compileJob)
|
||||
|
||||
+4
-3
@@ -46,14 +46,15 @@ func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = runJobs(job)
|
||||
jobs := append([]*compileJob{job}, job.dependencies...)
|
||||
err = runJobs(jobs)
|
||||
return job.result, err
|
||||
}
|
||||
|
||||
// load returns a compile job to build this library file for the given target
|
||||
// and CPU. It may return a dummy compileJob if the library build is already
|
||||
// cached. The path is stored as job.result but is only valid after the job has
|
||||
// been run.
|
||||
// cached. The path is stored as job.result but is only valid if the job and
|
||||
// job.dependencies have been run.
|
||||
// The provided tmpdir will be used to store intermediary files and possibly the
|
||||
// output archive file, it is expected to be removed after use.
|
||||
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
)
|
||||
|
||||
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrfutil%2FUG%2Fnrfutil%2Fnrfutil_intro.html
|
||||
|
||||
func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string) error {
|
||||
cmdLine := []string{"nrfutil", "pkg", "generate", "--hw-version", "52", "--sd-req", "0x0", "--debug-mode", "--application", infile, outfile}
|
||||
|
||||
if options.PrintCommands != nil {
|
||||
options.PrintCommands(cmdLine[0], cmdLine[1:]...)
|
||||
}
|
||||
|
||||
cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
|
||||
cmd.Stdout = ioutil.Discard
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -24,6 +25,14 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
||||
// platforms and Go versions.
|
||||
func normalizeResult(result string) string {
|
||||
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
||||
|
||||
// Make sure all functions are wrapped, even those that would otherwise be
|
||||
// single-line functions. This is necessary because Go 1.14 changed the way
|
||||
// such functions are wrapped and it's important to have consistent test
|
||||
// results.
|
||||
re := regexp.MustCompile(`func \((.+)\)( .*?) +{ (.+) }`)
|
||||
actual = re.ReplaceAllString(actual, "func ($1)$2 {\n\t$3\n}")
|
||||
|
||||
return actual
|
||||
}
|
||||
|
||||
|
||||
+74
-164
@@ -11,184 +11,105 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error)
|
||||
precedences = map[token.Token]int{
|
||||
token.ADD: precedenceAdd,
|
||||
token.SUB: precedenceAdd,
|
||||
token.MUL: precedenceMul,
|
||||
token.QUO: precedenceMul,
|
||||
token.REM: precedenceMul,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
precedenceLowest = iota + 1
|
||||
precedenceAdd
|
||||
precedenceMul
|
||||
precedencePrefix
|
||||
)
|
||||
|
||||
func init() {
|
||||
// This must be done in an init function to avoid an initialization order
|
||||
// failure.
|
||||
prefixParseFns = map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error){
|
||||
token.IDENT: parseIdent,
|
||||
token.INT: parseBasicLit,
|
||||
token.FLOAT: parseBasicLit,
|
||||
token.STRING: parseBasicLit,
|
||||
token.CHAR: parseBasicLit,
|
||||
token.LPAREN: parseParenExpr,
|
||||
token.SUB: parseUnaryExpr,
|
||||
}
|
||||
}
|
||||
|
||||
// parseConst parses the given string as a C constant.
|
||||
func parseConst(pos token.Pos, fset *token.FileSet, value string) (ast.Expr, *scanner.Error) {
|
||||
t := newTokenizer(pos, fset, value)
|
||||
expr, err := parseConstExpr(t, precedenceLowest)
|
||||
t.Next()
|
||||
if t.curToken != token.EOF {
|
||||
expr, err := parseConstExpr(t)
|
||||
if t.token != token.EOF {
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(t.curPos),
|
||||
Msg: "unexpected token " + t.curToken.String() + ", expected end of expression",
|
||||
Pos: t.fset.Position(t.pos),
|
||||
Msg: "unexpected token " + t.token.String(),
|
||||
}
|
||||
}
|
||||
return expr, err
|
||||
}
|
||||
|
||||
// parseConstExpr parses a stream of C tokens to a Go expression.
|
||||
func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
|
||||
if t.curToken == token.EOF {
|
||||
func parseConstExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
switch t.token {
|
||||
case token.LPAREN:
|
||||
lparen := t.pos
|
||||
t.Next()
|
||||
x, err := parseConstExpr(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.token != token.RPAREN {
|
||||
return nil, unexpectedToken(t, token.RPAREN)
|
||||
}
|
||||
expr := &ast.ParenExpr{
|
||||
Lparen: lparen,
|
||||
X: x,
|
||||
Rparen: t.pos,
|
||||
}
|
||||
t.Next()
|
||||
return expr, nil
|
||||
case token.INT, token.FLOAT, token.STRING, token.CHAR:
|
||||
expr := &ast.BasicLit{
|
||||
ValuePos: t.pos,
|
||||
Kind: t.token,
|
||||
Value: t.value,
|
||||
}
|
||||
t.Next()
|
||||
return expr, nil
|
||||
case token.IDENT:
|
||||
expr := &ast.Ident{
|
||||
NamePos: t.pos,
|
||||
Name: "C." + t.value,
|
||||
}
|
||||
t.Next()
|
||||
return expr, nil
|
||||
case token.EOF:
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(t.curPos),
|
||||
Pos: t.fset.Position(t.pos),
|
||||
Msg: "empty constant",
|
||||
}
|
||||
}
|
||||
prefix := prefixParseFns[t.curToken]
|
||||
if prefix == nil {
|
||||
default:
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(t.curPos),
|
||||
Msg: fmt.Sprintf("unexpected token %s", t.curToken),
|
||||
Pos: t.fset.Position(t.pos),
|
||||
Msg: fmt.Sprintf("unexpected token %s", t.token),
|
||||
}
|
||||
}
|
||||
leftExpr, err := prefix(t)
|
||||
|
||||
for t.peekToken != token.EOF && precedence < precedences[t.peekToken] {
|
||||
switch t.peekToken {
|
||||
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
|
||||
t.Next()
|
||||
leftExpr, err = parseBinaryExpr(t, leftExpr)
|
||||
}
|
||||
}
|
||||
|
||||
return leftExpr, err
|
||||
}
|
||||
|
||||
func parseIdent(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
return &ast.Ident{
|
||||
NamePos: t.curPos,
|
||||
Name: "C." + t.curValue,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseBasicLit(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
return &ast.BasicLit{
|
||||
ValuePos: t.curPos,
|
||||
Kind: t.curToken,
|
||||
Value: t.curValue,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseParenExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
lparen := t.curPos
|
||||
t.Next()
|
||||
x, err := parseConstExpr(t, precedenceLowest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.Next()
|
||||
if t.curToken != token.RPAREN {
|
||||
return nil, unexpectedToken(t, token.RPAREN)
|
||||
}
|
||||
expr := &ast.ParenExpr{
|
||||
Lparen: lparen,
|
||||
X: x,
|
||||
Rparen: t.curPos,
|
||||
}
|
||||
return expr, nil
|
||||
}
|
||||
|
||||
func parseBinaryExpr(t *tokenizer, left ast.Expr) (ast.Expr, *scanner.Error) {
|
||||
expression := &ast.BinaryExpr{
|
||||
X: left,
|
||||
Op: t.curToken,
|
||||
OpPos: t.curPos,
|
||||
}
|
||||
precedence := precedences[t.curToken]
|
||||
t.Next()
|
||||
right, err := parseConstExpr(t, precedence)
|
||||
expression.Y = right
|
||||
return expression, err
|
||||
}
|
||||
|
||||
func parseUnaryExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
expression := &ast.UnaryExpr{
|
||||
OpPos: t.curPos,
|
||||
Op: t.curToken,
|
||||
}
|
||||
t.Next()
|
||||
x, err := parseConstExpr(t, precedencePrefix)
|
||||
expression.X = x
|
||||
return expression, err
|
||||
}
|
||||
|
||||
// unexpectedToken returns an error of the form "unexpected token FOO, expected
|
||||
// BAR".
|
||||
func unexpectedToken(t *tokenizer, expected token.Token) *scanner.Error {
|
||||
return &scanner.Error{
|
||||
Pos: t.fset.Position(t.curPos),
|
||||
Msg: fmt.Sprintf("unexpected token %s, expected %s", t.curToken, expected),
|
||||
Pos: t.fset.Position(t.pos),
|
||||
Msg: fmt.Sprintf("unexpected token %s, expected %s", t.token, expected),
|
||||
}
|
||||
}
|
||||
|
||||
// tokenizer reads C source code and converts it to Go tokens.
|
||||
type tokenizer struct {
|
||||
curPos, peekPos token.Pos
|
||||
fset *token.FileSet
|
||||
curToken, peekToken token.Token
|
||||
curValue, peekValue string
|
||||
buf string
|
||||
pos token.Pos
|
||||
fset *token.FileSet
|
||||
token token.Token
|
||||
value string
|
||||
buf string
|
||||
}
|
||||
|
||||
// newTokenizer initializes a new tokenizer, positioned at the first token in
|
||||
// the string.
|
||||
func newTokenizer(start token.Pos, fset *token.FileSet, buf string) *tokenizer {
|
||||
t := &tokenizer{
|
||||
peekPos: start,
|
||||
fset: fset,
|
||||
buf: buf,
|
||||
peekToken: token.ILLEGAL,
|
||||
pos: start,
|
||||
fset: fset,
|
||||
buf: buf,
|
||||
token: token.ILLEGAL,
|
||||
}
|
||||
// Parse the first two tokens (cur and peek).
|
||||
t.Next()
|
||||
t.Next()
|
||||
t.Next() // Parse the first token.
|
||||
return t
|
||||
}
|
||||
|
||||
// Next consumes the next token in the stream. There is no return value, read
|
||||
// the next token from the pos, token and value properties.
|
||||
func (t *tokenizer) Next() {
|
||||
// The previous peek is now the current token.
|
||||
t.curPos = t.peekPos
|
||||
t.curToken = t.peekToken
|
||||
t.curValue = t.peekValue
|
||||
|
||||
// Parse the next peek token.
|
||||
t.peekPos += token.Pos(len(t.curValue))
|
||||
t.pos += token.Pos(len(t.value))
|
||||
for {
|
||||
if len(t.buf) == 0 {
|
||||
t.peekToken = token.EOF
|
||||
t.token = token.EOF
|
||||
return
|
||||
}
|
||||
c := t.buf[0]
|
||||
@@ -197,28 +118,17 @@ func (t *tokenizer) Next() {
|
||||
// Skip whitespace.
|
||||
// Based on this source, not sure whether it represents C whitespace:
|
||||
// https://en.cppreference.com/w/cpp/string/byte/isspace
|
||||
t.peekPos++
|
||||
t.pos++
|
||||
t.buf = t.buf[1:]
|
||||
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%':
|
||||
case c == '(' || c == ')':
|
||||
// Single-character tokens.
|
||||
// TODO: ++ (increment) and -- (decrement) operators.
|
||||
switch c {
|
||||
case '(':
|
||||
t.peekToken = token.LPAREN
|
||||
t.token = token.LPAREN
|
||||
case ')':
|
||||
t.peekToken = token.RPAREN
|
||||
case '+':
|
||||
t.peekToken = token.ADD
|
||||
case '-':
|
||||
t.peekToken = token.SUB
|
||||
case '*':
|
||||
t.peekToken = token.MUL
|
||||
case '/':
|
||||
t.peekToken = token.QUO
|
||||
case '%':
|
||||
t.peekToken = token.REM
|
||||
t.token = token.RPAREN
|
||||
}
|
||||
t.peekValue = t.buf[:1]
|
||||
t.value = t.buf[:1]
|
||||
t.buf = t.buf[1:]
|
||||
return
|
||||
case c >= '0' && c <= '9':
|
||||
@@ -236,17 +146,17 @@ func (t *tokenizer) Next() {
|
||||
break
|
||||
}
|
||||
}
|
||||
t.peekValue = t.buf[:tokenLen]
|
||||
t.value = t.buf[:tokenLen]
|
||||
t.buf = t.buf[tokenLen:]
|
||||
if hasDot {
|
||||
// Integer constants are more complicated than this but this is
|
||||
// a close approximation.
|
||||
// https://en.cppreference.com/w/cpp/language/integer_literal
|
||||
t.peekToken = token.FLOAT
|
||||
t.peekValue = strings.TrimRight(t.peekValue, "f")
|
||||
t.token = token.FLOAT
|
||||
t.value = strings.TrimRight(t.value, "f")
|
||||
} else {
|
||||
t.peekToken = token.INT
|
||||
t.peekValue = strings.TrimRight(t.peekValue, "uUlL")
|
||||
t.token = token.INT
|
||||
t.value = strings.TrimRight(t.value, "uUlL")
|
||||
}
|
||||
return
|
||||
case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_':
|
||||
@@ -260,9 +170,9 @@ func (t *tokenizer) Next() {
|
||||
break
|
||||
}
|
||||
}
|
||||
t.peekValue = t.buf[:tokenLen]
|
||||
t.value = t.buf[:tokenLen]
|
||||
t.buf = t.buf[tokenLen:]
|
||||
t.peekToken = token.IDENT
|
||||
t.token = token.IDENT
|
||||
return
|
||||
case c == '"':
|
||||
// String constant. Find the first '"' character that is not
|
||||
@@ -278,8 +188,8 @@ func (t *tokenizer) Next() {
|
||||
escape = c == '\\'
|
||||
}
|
||||
}
|
||||
t.peekToken = token.STRING
|
||||
t.peekValue = t.buf[:tokenLen]
|
||||
t.token = token.STRING
|
||||
t.value = t.buf[:tokenLen]
|
||||
t.buf = t.buf[tokenLen:]
|
||||
return
|
||||
case c == '\'':
|
||||
@@ -296,12 +206,12 @@ func (t *tokenizer) Next() {
|
||||
escape = c == '\\'
|
||||
}
|
||||
}
|
||||
t.peekToken = token.CHAR
|
||||
t.peekValue = t.buf[:tokenLen]
|
||||
t.token = token.CHAR
|
||||
t.value = t.buf[:tokenLen]
|
||||
t.buf = t.buf[tokenLen:]
|
||||
return
|
||||
default:
|
||||
t.peekToken = token.ILLEGAL
|
||||
t.token = token.ILLEGAL
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
+2
-19
@@ -18,7 +18,7 @@ func TestParseConst(t *testing.T) {
|
||||
{`(5)`, `(5)`},
|
||||
{`(((5)))`, `(5)`},
|
||||
{`)`, `error: 1:1: unexpected token )`},
|
||||
{`5)`, `error: 1:2: unexpected token ), expected end of expression`},
|
||||
{`5)`, `error: 1:2: unexpected token )`},
|
||||
{" \t)", `error: 1:4: unexpected token )`},
|
||||
{`5.8f`, `5.8`},
|
||||
{`foo`, `C.foo`},
|
||||
@@ -30,24 +30,7 @@ func TestParseConst(t *testing.T) {
|
||||
{`'a'`, `'a'`},
|
||||
{`0b10`, `0b10`},
|
||||
{`0x1234_5678`, `0x1234_5678`},
|
||||
{`5 5`, `error: 1:3: unexpected token INT, expected end of expression`}, // test for a bugfix
|
||||
// Binary operators.
|
||||
{`5+5`, `5 + 5`},
|
||||
{`5-5`, `5 - 5`},
|
||||
{`5*5`, `5 * 5`},
|
||||
{`5/5`, `5 / 5`},
|
||||
{`5%5`, `5 % 5`},
|
||||
{`(5/5)`, `(5 / 5)`},
|
||||
{`1 - 2`, `1 - 2`},
|
||||
{`1 - 2 + 3`, `1 - 2 + 3`},
|
||||
{`1 - 2 * 3`, `1 - 2*3`},
|
||||
{`(1 - 2) * 3`, `(1 - 2) * 3`},
|
||||
{`1 * 2 - 3`, `1*2 - 3`},
|
||||
{`1 * (2 - 3)`, `1 * (2 - 3)`},
|
||||
// Unary operators.
|
||||
{`-5`, `-5`},
|
||||
{`-5-2`, `-5 - 2`},
|
||||
{`5 - - 2`, `5 - -2`},
|
||||
{`5 5`, `error: 1:3: unexpected token INT`}, // test for a bugfix
|
||||
} {
|
||||
fset := token.NewFileSet()
|
||||
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// CGo errors:
|
||||
// testdata/errors.go:4:2: warning: some warning
|
||||
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
||||
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:13:23: unexpected token )
|
||||
|
||||
// Type checking errors after CGo processing:
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
||||
|
||||
Vendored
+15
-5
@@ -67,7 +67,9 @@ type C.union3_t = C.union_1
|
||||
type C.union_nested_t = C.union_3
|
||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
||||
|
||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_4) bitfield_a() C.uchar {
|
||||
return s.__bitfield_1 & 0x1f
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
@@ -103,9 +105,15 @@ type C.struct_type1 struct {
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
|
||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_i() *C.int {
|
||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_1) unionfield_d() *float64 {
|
||||
return (*float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_1) unionfield_s() *C.short {
|
||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_1 struct{ $union uint64 }
|
||||
|
||||
@@ -130,7 +138,9 @@ func (union *C.union_3) unionfield_thing() *C.union3_t {
|
||||
|
||||
type C.union_3 struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union2d) unionfield_i() *C.int {
|
||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
+13
-41
@@ -55,7 +55,7 @@ func (c *Config) GOARCH() string {
|
||||
|
||||
// BuildTags returns the complete list of build tags used during this build.
|
||||
func (c *Config) BuildTags() []string {
|
||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||
tags := append(c.Target.BuildTags, []string{"tinygo", "gc." + c.GC(), "scheduler." + c.Scheduler()}...)
|
||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
}
|
||||
@@ -80,7 +80,12 @@ func (c *Config) GC() string {
|
||||
if c.Target.GC != "" {
|
||||
return c.Target.GC
|
||||
}
|
||||
return "conservative"
|
||||
for _, tag := range c.Target.BuildTags {
|
||||
if tag == "baremetal" || tag == "wasm" {
|
||||
return "conservative"
|
||||
}
|
||||
}
|
||||
return "extalloc"
|
||||
}
|
||||
|
||||
// NeedsStackObjects returns true if the compiler should insert stack objects
|
||||
@@ -89,7 +94,7 @@ func (c *Config) NeedsStackObjects() bool {
|
||||
switch c.GC() {
|
||||
case "conservative", "extalloc":
|
||||
for _, tag := range c.BuildTags() {
|
||||
if tag == "tinygo.wasm" {
|
||||
if tag == "wasm" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -113,18 +118,6 @@ func (c *Config) Scheduler() string {
|
||||
return "coroutines"
|
||||
}
|
||||
|
||||
// Serial returns the serial implementation for this build configuration: uart,
|
||||
// usb (meaning USB-CDC), or none.
|
||||
func (c *Config) Serial() string {
|
||||
if c.Options.Serial != "" {
|
||||
return c.Options.Serial
|
||||
}
|
||||
if c.Target.Serial != "" {
|
||||
return c.Target.Serial
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
|
||||
// threshold as used in the LLVM optimization pipeline.
|
||||
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
||||
@@ -188,15 +181,6 @@ func (c *Config) AutomaticStackSize() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
||||
// calculates and patches in the checksum for the 2nd stage bootloader.
|
||||
func (c *Config) RP2040BootPatch() bool {
|
||||
if c.Target.RP2040BootPatch != nil {
|
||||
return *c.Target.RP2040BootPatch
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||
// preprocessing.
|
||||
func (c *Config) CFlags() []string {
|
||||
@@ -209,8 +193,9 @@ func (c *Config) CFlags() []string {
|
||||
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
||||
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
||||
}
|
||||
// Always emit debug information. It is optionally stripped at link time.
|
||||
cflags = append(cflags, "-g")
|
||||
if c.Debug() {
|
||||
cflags = append(cflags, "-g")
|
||||
}
|
||||
return cflags
|
||||
}
|
||||
|
||||
@@ -249,9 +234,8 @@ func (c *Config) VerifyIR() bool {
|
||||
return c.Options.VerifyIR
|
||||
}
|
||||
|
||||
// Debug returns whether debug (DWARF) information should be retained by the
|
||||
// linker. By default, debug information is retained but it can be removed with
|
||||
// the -no-debug flag.
|
||||
// Debug returns whether to add debug symbols to the IR, for debugging with GDB
|
||||
// and similar.
|
||||
func (c *Config) Debug() bool {
|
||||
return c.Options.Debug
|
||||
}
|
||||
@@ -275,11 +259,6 @@ func (c *Config) BinaryFormat(ext string) string {
|
||||
// More information:
|
||||
// https://github.com/Microsoft/uf2
|
||||
return "uf2"
|
||||
case ".zip":
|
||||
if c.Target.BinaryFormat != "" {
|
||||
return c.Target.BinaryFormat
|
||||
}
|
||||
return "zip"
|
||||
default:
|
||||
// Use the ELF format for unrecognized file formats.
|
||||
return "elf"
|
||||
@@ -297,9 +276,6 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
||||
case "openocd", "msd", "command":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||
case "bmp":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, ""
|
||||
default:
|
||||
// The -programmer flag specifies something else, assume it specifies
|
||||
// the OpenOCD interface name.
|
||||
@@ -366,10 +342,6 @@ func (c *Config) WasmAbi() string {
|
||||
return c.Target.WasmAbi
|
||||
}
|
||||
|
||||
func (c *Config) LLVMFeatures() string {
|
||||
return c.Options.LLVMFeatures
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
|
||||
+18
-31
@@ -9,7 +9,6 @@ import (
|
||||
var (
|
||||
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
|
||||
validSerialOptions = []string{"none", "uart", "usb"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||
@@ -18,27 +17,24 @@ var (
|
||||
// Options contains extra options to give to the compiler. These options are
|
||||
// usually passed from the command line.
|
||||
type Options struct {
|
||||
Target string
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
Serial string
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands func(cmd string, args ...string)
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags string
|
||||
WasmAbi string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Target string
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags string
|
||||
WasmAbi string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
@@ -61,15 +57,6 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
}
|
||||
|
||||
if o.Serial != "" {
|
||||
valid := isInArray(validSerialOptions, o.Serial)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
||||
o.Serial,
|
||||
strings.Join(validSerialOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
if o.PrintSizes != "" {
|
||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
||||
if !valid {
|
||||
|
||||
+8
-19
@@ -31,7 +31,6 @@ type TargetSpec struct {
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Serial string `json:"serial"` // which serial output to use (uart, usb, none)
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc"`
|
||||
@@ -41,12 +40,10 @@ type TargetSpec struct {
|
||||
LDFlags []string `json:"ldflags"`
|
||||
LinkerScript string `json:"linkerscript"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||
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"`
|
||||
SerialPort []string `json:"serial-port"` // serial port IDs in the form "acm:vid:pid" or "usb:vid:pid"
|
||||
FlashMethod string `json:"flash-method"`
|
||||
FlashVolume string `json:"msd-volume-name"`
|
||||
FlashFilename string `json:"msd-firmware-name"`
|
||||
@@ -172,7 +169,6 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
||||
"386": "i386",
|
||||
"amd64": "x86_64",
|
||||
"arm64": "aarch64",
|
||||
"arm": "thumbv7",
|
||||
}[goarch]
|
||||
if llvmarch == "" {
|
||||
llvmarch = goarch
|
||||
@@ -243,29 +239,22 @@ 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},
|
||||
Scheduler: "tasks",
|
||||
Linker: "cc",
|
||||
DefaultStackSize: 1024 * 64, // 64kB
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
}
|
||||
if goarch == "386" {
|
||||
spec.CPU = "pentium4"
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Linker: "cc",
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||
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")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+".S")
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package compiler
|
||||
|
||||
// This file defines alias functions for functions that are normally defined in
|
||||
// Go assembly.
|
||||
//
|
||||
// The Go toolchain defines many performance critical functions in assembly
|
||||
// instead of plain Go. This is a problem for TinyGo as it currently (as of
|
||||
// august 2021) is not able to compile these assembly files and even if it
|
||||
// could, it would not be able to make use of them for many targets that are
|
||||
// supported by TinyGo (baremetal RISC-V, AVR, etc). Therefore, many of these
|
||||
// functions are aliased to their generic Go implementation.
|
||||
// This results in slower than possible implementations, but at least they are
|
||||
// usable.
|
||||
|
||||
import "tinygo.org/x/go-llvm"
|
||||
|
||||
var stdlibAliases = map[string]string{
|
||||
// crypto packages
|
||||
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||
"crypto/sha256.block": "crypto/sha256.blockGeneric",
|
||||
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||
|
||||
// math package
|
||||
"math.Asin": "math.asin",
|
||||
"math.Asinh": "math.asinh",
|
||||
"math.Acos": "math.acos",
|
||||
"math.Acosh": "math.acosh",
|
||||
"math.Atan": "math.atan",
|
||||
"math.Atanh": "math.atanh",
|
||||
"math.Atan2": "math.atan2",
|
||||
"math.Cbrt": "math.cbrt",
|
||||
"math.Ceil": "math.ceil",
|
||||
"math.archCeil": "math.ceil",
|
||||
"math.Cos": "math.cos",
|
||||
"math.Cosh": "math.cosh",
|
||||
"math.Erf": "math.erf",
|
||||
"math.Erfc": "math.erfc",
|
||||
"math.Exp": "math.exp",
|
||||
"math.archExp": "math.exp",
|
||||
"math.Expm1": "math.expm1",
|
||||
"math.Exp2": "math.exp2",
|
||||
"math.archExp2": "math.exp2",
|
||||
"math.Floor": "math.floor",
|
||||
"math.archFloor": "math.floor",
|
||||
"math.Frexp": "math.frexp",
|
||||
"math.Hypot": "math.hypot",
|
||||
"math.archHypot": "math.hypot",
|
||||
"math.Ldexp": "math.ldexp",
|
||||
"math.Log": "math.log",
|
||||
"math.archLog": "math.log",
|
||||
"math.Log1p": "math.log1p",
|
||||
"math.Log10": "math.log10",
|
||||
"math.Log2": "math.log2",
|
||||
"math.Max": "math.max",
|
||||
"math.archMax": "math.max",
|
||||
"math.Min": "math.min",
|
||||
"math.archMin": "math.min",
|
||||
"math.Mod": "math.mod",
|
||||
"math.Modf": "math.modf",
|
||||
"math.archModf": "math.modf",
|
||||
"math.Pow": "math.pow",
|
||||
"math.Remainder": "math.remainder",
|
||||
"math.Sin": "math.sin",
|
||||
"math.Sinh": "math.sinh",
|
||||
"math.Sqrt": "math.sqrt",
|
||||
"math.archSqrt": "math.sqrt",
|
||||
"math.Tan": "math.tan",
|
||||
"math.Tanh": "math.tanh",
|
||||
"math.Trunc": "math.trunc",
|
||||
"math.archTrunc": "math.trunc",
|
||||
}
|
||||
|
||||
// createAlias implements the function (in the builder) as a call to the alias
|
||||
// function.
|
||||
func (b *builder) createAlias(alias llvm.Value) {
|
||||
if b.Debug {
|
||||
if b.fn.Syntax() != nil {
|
||||
// Create debug info file if present.
|
||||
b.difunc = b.attachDebugInfo(b.fn)
|
||||
}
|
||||
pos := b.program.Fset.Position(b.fn.Pos())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
||||
b.SetInsertPointAtEnd(entryBlock)
|
||||
if b.llvmFn.Type() != alias.Type() {
|
||||
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||
b.CreateUnreachable()
|
||||
return
|
||||
}
|
||||
result := b.CreateCall(alias, b.llvmFn.Params(), "")
|
||||
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateRetVoid()
|
||||
} else {
|
||||
b.CreateRet(result)
|
||||
}
|
||||
}
|
||||
@@ -101,55 +101,6 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
|
||||
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
|
||||
}
|
||||
|
||||
// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
|
||||
// conversions. This conversion was added in Go 1.17. For details, see:
|
||||
// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
|
||||
func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen int64) {
|
||||
// From the spec:
|
||||
// > If the length of the slice is less than the length of the array, a
|
||||
// > run-time panic occurs.
|
||||
arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
||||
isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
|
||||
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
||||
}
|
||||
|
||||
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
|
||||
// function must panic if the ptr/len parameters are invalid.
|
||||
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
|
||||
// From the documentation of unsafe.Slice:
|
||||
// > At run time, if len is negative, or if ptr is nil and len is not
|
||||
// > zero, a run-time panic occurs.
|
||||
// However, in practice, it is also necessary to check that the length is
|
||||
// not too big that a GEP wouldn't be possible without wrapping the pointer.
|
||||
// These two checks (non-negative and not too big) can be merged into one
|
||||
// using an unsiged greater than.
|
||||
|
||||
// Make sure the len value is at least as big as a uintptr.
|
||||
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||
if lenType.Info()&types.IsUnsigned != 0 {
|
||||
len = b.CreateZExt(len, b.uintptrType, "")
|
||||
} else {
|
||||
len = b.CreateSExt(len, b.uintptrType, "")
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the maximum slice size, and therefore the maximum value of the
|
||||
// len parameter.
|
||||
maxSize := b.maxSliceSize(ptr.Type().ElementType())
|
||||
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
||||
|
||||
// Do the check. By using unsigned greater than for the length check, signed
|
||||
// negative values are also checked (which are very large numbers when
|
||||
// interpreted as signed values).
|
||||
zero := llvm.ConstInt(len.Type(), 0, false)
|
||||
lenOutOfBounds := b.CreateICmp(llvm.IntUGT, len, maxSizeValue, "")
|
||||
ptrIsNil := b.CreateICmp(llvm.IntEQ, ptr, llvm.ConstNull(ptr.Type()), "")
|
||||
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
|
||||
}
|
||||
|
||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||
// check that the value is not too big for runtime.chanMake.
|
||||
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
|
||||
@@ -211,10 +162,6 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str
|
||||
case *ssa.Alloc:
|
||||
// An alloc is never nil.
|
||||
return
|
||||
case *ssa.FreeVar:
|
||||
// A free variable is allocated in a parent function and is thus never
|
||||
// nil.
|
||||
return
|
||||
case *ssa.IndexAddr:
|
||||
// This pointer is the result of an index operation into a slice or
|
||||
// array. Such slices/arrays are already bounds checked so the pointer
|
||||
|
||||
+9
-28
@@ -32,15 +32,8 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
|
||||
// store value-to-send
|
||||
valueType := b.getLLVMType(instr.X.Type())
|
||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
||||
if isZeroSize {
|
||||
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||
} else {
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
b.CreateStore(chanValue, valueAlloca)
|
||||
}
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
||||
b.CreateStore(chanValue, valueAlloca)
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
@@ -53,9 +46,7 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||
if !isZeroSize {
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
}
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
}
|
||||
|
||||
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||
@@ -65,14 +56,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
ch := b.getValue(unop.X)
|
||||
|
||||
// Allocate memory to receive into.
|
||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
||||
if isZeroSize {
|
||||
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||
} else {
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
}
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
@@ -80,14 +64,9 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
|
||||
// Do the receive.
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||
var received llvm.Value
|
||||
if isZeroSize {
|
||||
received = llvm.ConstNull(valueType)
|
||||
} else {
|
||||
received = b.CreateLoad(valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
}
|
||||
received := b.CreateLoad(valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
|
||||
if unop.CommaOk {
|
||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
||||
@@ -137,6 +116,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
// determine the receive buffer size and alignment.
|
||||
recvbufSize := uint64(0)
|
||||
recvbufAlign := 0
|
||||
hasReceives := false
|
||||
var selectStates []llvm.Value
|
||||
chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
|
||||
for _, state := range expr.States {
|
||||
@@ -153,6 +133,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
|
||||
recvbufAlign = align
|
||||
}
|
||||
hasReceives = true
|
||||
case types.SendOnly:
|
||||
// Store this value in an alloca and put a pointer to this alloca
|
||||
// in the send state.
|
||||
@@ -169,7 +150,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
|
||||
// Create a receive buffer, where the received value will be stored.
|
||||
recvbuf := llvm.Undef(b.i8ptrType)
|
||||
if recvbufSize != 0 {
|
||||
if hasReceives {
|
||||
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
||||
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
||||
recvbufAlloca.SetAlignment(recvbufAlign)
|
||||
|
||||
+65
-190
@@ -23,7 +23,7 @@ import (
|
||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||
// package changes in a way that affects the generated LLVM module.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 19 // last change: fix channel ops with zero values
|
||||
const Version = 9 // last change: implement reflect.New()
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
@@ -59,7 +59,6 @@ type Config struct {
|
||||
DefaultStackSize uint64
|
||||
NeedsStackObjects bool
|
||||
Debug bool // Whether to emit debug information in the LLVM module.
|
||||
LLVMFeatures string
|
||||
}
|
||||
|
||||
// compilerContext contains function-independent data that should still be
|
||||
@@ -74,7 +73,6 @@ type compilerContext struct {
|
||||
cu llvm.Metadata
|
||||
difiles map[string]llvm.Metadata
|
||||
ditypes map[types.Type]llvm.Metadata
|
||||
llvmTypes map[types.Type]llvm.Type
|
||||
machine llvm.TargetMachine
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
@@ -95,7 +93,6 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
||||
DumpSSA: dumpSSA,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
llvmTypes: make(map[types.Type]llvm.Type),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
astComments: map[string]*ast.CommentGroup{},
|
||||
@@ -188,12 +185,7 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
|
||||
if err != nil {
|
||||
return llvm.TargetMachine{}, err
|
||||
}
|
||||
|
||||
feat := config.Features
|
||||
if len(config.LLVMFeatures) > 0 {
|
||||
feat = append(feat, config.LLVMFeatures)
|
||||
}
|
||||
features := strings.Join(feat, ",")
|
||||
features := strings.Join(config.Features, ",")
|
||||
|
||||
var codeModel llvm.CodeModel
|
||||
var relocationModel llvm.RelocMode
|
||||
@@ -317,23 +309,10 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
||||
return c.getLLVMType(typ)
|
||||
}
|
||||
|
||||
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
|
||||
// created types. This is somewhat important for performance, but especially
|
||||
// important for named struct types (which should only be created once).
|
||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
||||
// named struct types (or Go types implemented as named LLVM structs such as
|
||||
// strings) it also creates it first if necessary.
|
||||
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
// Try to load the LLVM type from the cache.
|
||||
if t, ok := c.llvmTypes[goType]; ok {
|
||||
return t
|
||||
}
|
||||
// Not already created, so adding this type to the cache.
|
||||
llvmType := c.makeLLVMType(goType)
|
||||
c.llvmTypes[goType] = llvmType
|
||||
return llvmType
|
||||
}
|
||||
|
||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||
// getLLVMType instead.
|
||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||
switch typ := goType.(type) {
|
||||
case *types.Array:
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
@@ -382,10 +361,12 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||
// LLVM. This is because it is otherwise impossible to create
|
||||
// self-referencing types such as linked lists.
|
||||
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
||||
llvmType := c.ctx.StructCreateNamed(llvmName)
|
||||
c.llvmTypes[goType] = llvmType // avoid infinite recursion
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
llvmType := c.mod.GetTypeByName(llvmName)
|
||||
if llvmType.IsNil() {
|
||||
llvmType = c.ctx.StructCreateNamed(llvmName)
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
}
|
||||
return llvmType
|
||||
}
|
||||
return c.getLLVMType(typ.Underlying())
|
||||
@@ -728,11 +709,11 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
if member.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
b.createFunction()
|
||||
case *ssa.Type:
|
||||
if types.IsInterface(member.Type()) {
|
||||
@@ -771,36 +752,10 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
case *ssa.Global:
|
||||
// Global variable.
|
||||
info := c.getGlobalInfo(member)
|
||||
global := c.getGlobal(member)
|
||||
if !info.extern {
|
||||
global := c.getGlobal(member)
|
||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
if info.section != "" {
|
||||
global.SetSection(info.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add forwarding functions for functions that would otherwise be
|
||||
// implemented in assembly.
|
||||
for _, name := range members {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
if member.Blocks != nil {
|
||||
continue // external function
|
||||
}
|
||||
info := c.getFunctionInfo(member)
|
||||
if aliasName, ok := stdlibAliases[info.linkName]; ok {
|
||||
alias := c.mod.NamedFunction(aliasName)
|
||||
if alias.IsNil() {
|
||||
// Shouldn't happen, but perhaps best to just ignore.
|
||||
// The error will be a link error, if there is an error.
|
||||
continue
|
||||
}
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
b.createAlias(alias)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -826,9 +781,6 @@ func (b *builder) createFunction() {
|
||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||
b.llvmFn.SetUnnamedAddr(true)
|
||||
}
|
||||
if b.info.section != "" {
|
||||
b.llvmFn.SetSection(b.info.section)
|
||||
}
|
||||
if b.info.exported && strings.HasPrefix(b.Triple, "wasm") {
|
||||
// Set the exported name. This is necessary for WebAssembly because
|
||||
// otherwise the function is not exported.
|
||||
@@ -1012,61 +964,11 @@ func (b *builder) createFunction() {
|
||||
}
|
||||
}
|
||||
|
||||
// posser is an interface that's implemented by both ssa.Value and
|
||||
// ssa.Instruction. It is implemented by everything that has a Pos() method,
|
||||
// which is all that getPos() needs.
|
||||
type posser interface {
|
||||
Pos() token.Pos
|
||||
}
|
||||
|
||||
// getPos returns position information for a ssa.Value or ssa.Instruction.
|
||||
//
|
||||
// Not all instructions have position information, especially when they're
|
||||
// implicit (such as implicit casts or implicit returns at the end of a
|
||||
// function). In these cases, it makes sense to try a bit harder to guess what
|
||||
// the position really should be.
|
||||
func getPos(val posser) token.Pos {
|
||||
pos := val.Pos()
|
||||
if pos != token.NoPos {
|
||||
// Easy: position is known.
|
||||
return pos
|
||||
}
|
||||
|
||||
// No position information is known.
|
||||
switch val := val.(type) {
|
||||
case *ssa.MakeInterface:
|
||||
return getPos(val.X)
|
||||
case *ssa.MakeClosure:
|
||||
return val.Fn.(*ssa.Function).Pos()
|
||||
case *ssa.Return:
|
||||
syntax := val.Parent().Syntax()
|
||||
if syntax != nil {
|
||||
// non-synthetic
|
||||
return syntax.End()
|
||||
}
|
||||
return token.NoPos
|
||||
case *ssa.FieldAddr:
|
||||
return getPos(val.X)
|
||||
case *ssa.IndexAddr:
|
||||
return getPos(val.X)
|
||||
case *ssa.Slice:
|
||||
return getPos(val.X)
|
||||
case *ssa.Store:
|
||||
return getPos(val.Addr)
|
||||
case *ssa.Extract:
|
||||
return getPos(val.Tuple)
|
||||
default:
|
||||
// This is reachable, for example with *ssa.Const, *ssa.If, and
|
||||
// *ssa.Jump. They might be implemented in some way in the future.
|
||||
return token.NoPos
|
||||
}
|
||||
}
|
||||
|
||||
// createInstruction builds the LLVM IR equivalent instructions for the
|
||||
// particular Go SSA instruction.
|
||||
func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(getPos(instr))
|
||||
pos := b.program.Fset.Position(instr.Pos())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
@@ -1092,8 +994,52 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
case *ssa.Defer:
|
||||
b.createDefer(instr)
|
||||
case *ssa.Go:
|
||||
// Get all function parameters to pass to the goroutine.
|
||||
var params []llvm.Value
|
||||
for _, param := range instr.Call.Args {
|
||||
params = append(params, b.getValue(param))
|
||||
}
|
||||
|
||||
// Start a new goroutine.
|
||||
b.createGo(instr)
|
||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||
// Static callee is known. This makes it easier to start a new
|
||||
// goroutine.
|
||||
var context llvm.Value
|
||||
switch value := instr.Call.Value.(type) {
|
||||
case *ssa.Function:
|
||||
// Goroutine call is regular function call. No context is necessary.
|
||||
context = llvm.Undef(b.i8ptrType)
|
||||
case *ssa.MakeClosure:
|
||||
// A goroutine call on a func value, but the callee is trivial to find. For
|
||||
// example: immediately applied functions.
|
||||
funcValue := b.getValue(value)
|
||||
context = b.extractFuncContext(funcValue)
|
||||
default:
|
||||
panic("StaticCallee returned an unexpected value")
|
||||
}
|
||||
params = append(params, context) // context parameter
|
||||
b.createGoInstruction(b.getFunction(callee), params, "", callee.Pos())
|
||||
} else if !instr.Call.IsInvoke() {
|
||||
// This is a function pointer.
|
||||
// At the moment, two extra params are passed to the newly started
|
||||
// goroutine:
|
||||
// * The function context, for closures.
|
||||
// * The function pointer (for tasks).
|
||||
funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
params = append(params, context) // context parameter
|
||||
switch b.Scheduler {
|
||||
case "none", "coroutines":
|
||||
// There are no additional parameters needed for the goroutine start operation.
|
||||
case "tasks":
|
||||
// Add the function pointer as a parameter to start the goroutine.
|
||||
params = append(params, funcPtr)
|
||||
default:
|
||||
panic("unknown scheduler type")
|
||||
}
|
||||
b.createGoInstruction(funcPtr, params, b.fn.RelString(nil), instr.Pos())
|
||||
} else {
|
||||
b.addError(instr.Pos(), "todo: go on interface call")
|
||||
}
|
||||
case *ssa.If:
|
||||
cond := b.getValue(instr.Cond)
|
||||
block := instr.Block()
|
||||
@@ -1306,38 +1252,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
case "ssa:wrapnilchk":
|
||||
// TODO: do an actual nil check?
|
||||
return argValues[0], nil
|
||||
|
||||
// Builtins from the unsafe package.
|
||||
case "Add": // unsafe.Add
|
||||
// This is basically just a GEP operation.
|
||||
// Note: the pointer is always of type *i8.
|
||||
ptr := argValues[0]
|
||||
len := argValues[1]
|
||||
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
|
||||
case "Slice": // unsafe.Slice
|
||||
// This creates a slice from a pointer and a length.
|
||||
// Note that the exception mentioned in the documentation (if the
|
||||
// pointer and length are nil, the slice is also nil) is trivially
|
||||
// already the case.
|
||||
ptr := argValues[0]
|
||||
len := argValues[1]
|
||||
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
|
||||
ptr.Type(),
|
||||
b.uintptrType,
|
||||
b.uintptrType,
|
||||
}, false))
|
||||
b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
|
||||
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||
// Too small, zero-extend len.
|
||||
len = b.CreateZExt(len, b.uintptrType, "")
|
||||
} else if len.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
||||
// Too big, truncate len.
|
||||
len = b.CreateTrunc(len, b.uintptrType, "")
|
||||
}
|
||||
slice = b.CreateInsertValue(slice, ptr, 0, "")
|
||||
slice = b.CreateInsertValue(slice, len, 1, "")
|
||||
slice = b.CreateInsertValue(slice, len, 2, "")
|
||||
return slice, nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
||||
}
|
||||
@@ -1366,11 +1280,6 @@ 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 == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
|
||||
result, ok := b.createMathOp(instr)
|
||||
if ok {
|
||||
return result, nil
|
||||
}
|
||||
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/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||
@@ -1383,8 +1292,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.emitCSROperation(instr)
|
||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||
return b.createSyscall(instr)
|
||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
||||
return b.createRawSyscallNoError(instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
return b.createVolatileLoad(instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
@@ -1484,28 +1391,6 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
|
||||
}
|
||||
}
|
||||
|
||||
// maxSliceSize determines the maximum size a slice of the given element type
|
||||
// can be.
|
||||
func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
||||
// Calculate ^uintptr(0), which is the max value that fits in uintptr.
|
||||
maxPointerValue := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
|
||||
// Calculate (^uint(0))/2, which is the max value that fits in an int.
|
||||
maxIntegerValue := llvm.ConstNot(llvm.ConstInt(c.intType, 0, false)).ZExtValue() / 2
|
||||
|
||||
// Determine the maximum allowed size for a slice. The biggest possible
|
||||
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
|
||||
// divide by the element type to get the real maximum size.
|
||||
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
|
||||
|
||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||
// an int.
|
||||
if maxSize > maxIntegerValue {
|
||||
maxSize = maxIntegerValue
|
||||
}
|
||||
|
||||
return maxSize
|
||||
}
|
||||
|
||||
// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
|
||||
// or multiple LLVM IR instructions and/or runtime calls.
|
||||
func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
@@ -1719,8 +1604,10 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
elemSize := b.targetData.TypeAllocSize(llvmElemType)
|
||||
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
|
||||
|
||||
maxSize := b.maxSliceSize(llvmElemType)
|
||||
if elemSize > maxSize {
|
||||
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in
|
||||
// uintptr if uintptr were signed.
|
||||
maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false))
|
||||
if elemSize > maxSize.ZExtValue() {
|
||||
// This seems to be checked by the typechecker already, but let's
|
||||
// check it again just to be sure.
|
||||
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
|
||||
@@ -1729,8 +1616,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
// Bounds checking.
|
||||
lenType := expr.Len.Type().Underlying().(*types.Basic)
|
||||
capType := expr.Cap.Type().Underlying().(*types.Basic)
|
||||
maxSizeValue := llvm.ConstInt(b.uintptrType, maxSize, false)
|
||||
b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||
b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||
|
||||
// Allocate the backing array.
|
||||
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
||||
@@ -1973,17 +1859,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
||||
}
|
||||
case *ssa.SliceToArrayPointer:
|
||||
// Conversion from a slice to an array pointer, as the name clearly
|
||||
// says. This requires a runtime check to make sure the slice is at
|
||||
// least as big as the array.
|
||||
slice := b.getValue(expr.X)
|
||||
sliceLen := b.CreateExtractValue(slice, 1, "")
|
||||
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
|
||||
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
|
||||
ptr := b.CreateExtractValue(slice, 0, "")
|
||||
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
|
||||
return ptr, nil
|
||||
case *ssa.TypeAssert:
|
||||
return b.createTypeAssert(expr), nil
|
||||
case *ssa.UnOp:
|
||||
|
||||
+36
-69
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -17,16 +16,9 @@ import (
|
||||
// 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")
|
||||
|
||||
type testCase struct {
|
||||
file string
|
||||
target string
|
||||
}
|
||||
|
||||
// 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) {
|
||||
t.Parallel()
|
||||
|
||||
// Check LLVM version.
|
||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
if err != nil {
|
||||
@@ -40,64 +32,43 @@ func TestCompiler(t *testing.T) {
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{"basic.go", ""},
|
||||
{"pointer.go", ""},
|
||||
{"slice.go", ""},
|
||||
{"string.go", ""},
|
||||
{"float.go", ""},
|
||||
{"interface.go", ""},
|
||||
{"func.go", ""},
|
||||
{"pragma.go", ""},
|
||||
{"goroutine.go", "wasm"},
|
||||
{"goroutine.go", "cortex-m-qemu"},
|
||||
{"channel.go", ""},
|
||||
{"intrinsics.go", "cortex-m-qemu"},
|
||||
{"intrinsics.go", "wasm"},
|
||||
}
|
||||
|
||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
target, err := compileopts.LoadTarget("i686--linux")
|
||||
if err != nil {
|
||||
t.Fatal("could not read Go version:", err)
|
||||
t.Fatal("failed to load target:", err)
|
||||
}
|
||||
if minor >= 17 {
|
||||
tests = append(tests, testCase{"go1.17.go", ""})
|
||||
config := &compileopts.Config{
|
||||
Options: &compileopts.Options{},
|
||||
Target: target,
|
||||
}
|
||||
compilerConfig := &Config{
|
||||
Triple: config.Triple(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
Scheduler: config.Scheduler(),
|
||||
FuncImplementation: config.FuncImplementation(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
}
|
||||
machine, err := NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
name := tc.file
|
||||
targetString := "wasm"
|
||||
if tc.target != "" {
|
||||
targetString = tc.target
|
||||
name = tc.file + "-" + tc.target
|
||||
}
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
target, err := compileopts.LoadTarget(targetString)
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target:", err)
|
||||
}
|
||||
config := &compileopts.Config{
|
||||
Options: &compileopts.Options{},
|
||||
Target: target,
|
||||
}
|
||||
compilerConfig := &Config{
|
||||
Triple: config.Triple(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
Scheduler: config.Scheduler(),
|
||||
FuncImplementation: config.FuncImplementation(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
}
|
||||
machine, err := NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
tests := []string{
|
||||
"basic.go",
|
||||
"pointer.go",
|
||||
"slice.go",
|
||||
"string.go",
|
||||
"float.go",
|
||||
"interface.go",
|
||||
"func.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/" + tc.file}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + testCase}, config.ClangHeaders, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -105,13 +76,13 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse test case %s: %s", tc.file, err)
|
||||
t.Fatalf("could not parse test case %s: %s", testCase, err)
|
||||
}
|
||||
|
||||
// Compile AST to IR.
|
||||
program := lprogram.LoadSSA()
|
||||
pkg := lprogram.MainPkg()
|
||||
mod, errs := CompilePackage(tc.file, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
if errs != nil {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
@@ -134,22 +105,18 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
|
||||
outFilePrefix := tc.file[:len(tc.file)-3]
|
||||
if tc.target != "" {
|
||||
outFilePrefix += "-" + tc.target
|
||||
}
|
||||
outPath := "./testdata/" + outFilePrefix + ".ll"
|
||||
outfile := "./testdata/" + testCase[:len(testCase)-3] + ".ll"
|
||||
|
||||
// Update test if needed. Do not check the result.
|
||||
if *flagUpdate {
|
||||
err := ioutil.WriteFile(outPath, []byte(mod.String()), 0666)
|
||||
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(outPath)
|
||||
expected, err := ioutil.ReadFile(outfile)
|
||||
if err != nil {
|
||||
t.Fatal("failed to read golden file:", err)
|
||||
}
|
||||
|
||||
+15
-117
@@ -5,110 +5,25 @@ package compiler
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createGo emits code to start a new goroutine.
|
||||
func (b *builder) createGo(instr *ssa.Go) {
|
||||
// Get all function parameters to pass to the goroutine.
|
||||
var params []llvm.Value
|
||||
for _, param := range instr.Call.Args {
|
||||
params = append(params, b.getValue(param))
|
||||
}
|
||||
|
||||
var prefix string
|
||||
var funcPtr llvm.Value
|
||||
hasContext := false
|
||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||
// Static callee is known. This makes it easier to start a new
|
||||
// goroutine.
|
||||
var context llvm.Value
|
||||
switch value := instr.Call.Value.(type) {
|
||||
case *ssa.Function:
|
||||
// Goroutine call is regular function call. No context is necessary.
|
||||
if b.Scheduler == "coroutines" {
|
||||
// The context parameter is assumed to be always present in the
|
||||
// coroutines scheduler.
|
||||
context = llvm.Undef(b.i8ptrType)
|
||||
}
|
||||
case *ssa.MakeClosure:
|
||||
// A goroutine call on a func value, but the callee is trivial to find. For
|
||||
// example: immediately applied functions.
|
||||
funcValue := b.getValue(value)
|
||||
context = b.extractFuncContext(funcValue)
|
||||
default:
|
||||
panic("StaticCallee returned an unexpected value")
|
||||
}
|
||||
if !context.IsNil() {
|
||||
params = append(params, context) // context parameter
|
||||
hasContext = true
|
||||
}
|
||||
funcPtr = b.getFunction(callee)
|
||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||
// We cheat. None of the builtins do any long or blocking operation, so
|
||||
// we might as well run these builtins right away without the program
|
||||
// noticing the difference.
|
||||
// Possible exceptions:
|
||||
// - copy: this is a possibly long operation, but not a blocking
|
||||
// operation. Semantically it makes no difference to run it right
|
||||
// away (not in a goroutine). However, in practice it makes no sense
|
||||
// to run copy in a goroutine as there is no way to (safely) know
|
||||
// when it is finished.
|
||||
// - panic: the error message would appear in the parent goroutine.
|
||||
// But because `go panic("err")` would halt the program anyway
|
||||
// (there is no recover), panicking right away would give the same
|
||||
// behavior as creating a goroutine, switching the scheduler to that
|
||||
// goroutine, and panicking there. So this optimization seems
|
||||
// correct.
|
||||
// - recover: because it runs in a new goroutine, it is never a
|
||||
// deferred function. Thus this is a no-op.
|
||||
if builtin.Name() == "recover" {
|
||||
// This is a no-op, even in a deferred function:
|
||||
// go recover()
|
||||
return
|
||||
}
|
||||
var argTypes []types.Type
|
||||
var argValues []llvm.Value
|
||||
for _, arg := range instr.Call.Args {
|
||||
argTypes = append(argTypes, arg.Type())
|
||||
argValues = append(argValues, b.getValue(arg))
|
||||
}
|
||||
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
|
||||
return
|
||||
} else if !instr.Call.IsInvoke() {
|
||||
// This is a function pointer.
|
||||
// At the moment, two extra params are passed to the newly started
|
||||
// goroutine:
|
||||
// * The function context, for closures.
|
||||
// * The function pointer (for tasks).
|
||||
var context llvm.Value
|
||||
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
params = append(params, context) // context parameter
|
||||
hasContext = true
|
||||
switch b.Scheduler {
|
||||
case "none", "coroutines":
|
||||
// There are no additional parameters needed for the goroutine start operation.
|
||||
case "tasks":
|
||||
// Add the function pointer as a parameter to start the goroutine.
|
||||
params = append(params, funcPtr)
|
||||
default:
|
||||
panic("unknown scheduler type")
|
||||
}
|
||||
prefix = b.fn.RelString(nil)
|
||||
} else {
|
||||
b.addError(instr.Pos(), "todo: go on interface call")
|
||||
return
|
||||
}
|
||||
|
||||
// createGoInstruction starts a new goroutine with the provided function pointer
|
||||
// and parameters.
|
||||
// In general, you should pass all regular parameters plus the context parameter.
|
||||
// There is one exception: the task-based scheduler needs to have the function
|
||||
// pointer passed in as a parameter too in addition to the context.
|
||||
//
|
||||
// Because a go statement doesn't return anything, return undef.
|
||||
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
|
||||
paramBundle := b.emitPointerPack(params)
|
||||
var callee, stackSize llvm.Value
|
||||
switch b.Scheduler {
|
||||
case "none", "tasks":
|
||||
callee = b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
|
||||
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
|
||||
if b.AutomaticStackSize {
|
||||
// The stack size is not known until after linking. Call a dummy
|
||||
// function that will be replaced with a load from a special ELF
|
||||
@@ -119,9 +34,6 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
} else {
|
||||
// The stack size is fixed at compile time. By emitting it here as a
|
||||
// constant, it can be optimized.
|
||||
if b.Scheduler == "tasks" && b.DefaultStackSize == 0 {
|
||||
b.addError(instr.Pos(), "default stack size for goroutines is not set")
|
||||
}
|
||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||
}
|
||||
case "coroutines":
|
||||
@@ -134,6 +46,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
}
|
||||
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
|
||||
return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
|
||||
}
|
||||
|
||||
// createGoroutineStartWrapper creates a wrapper for the task-based
|
||||
@@ -154,12 +67,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
// allows a single (pointer) argument to the newly started goroutine. Also, it
|
||||
// ignores the return value because newly started goroutines do not have a
|
||||
// return value.
|
||||
//
|
||||
// The hasContext parameter indicates whether the context parameter (the second
|
||||
// to last parameter of the function) is used for this wrapper. If hasContext is
|
||||
// false, the parameter bundle is assumed to have no context parameter and undef
|
||||
// is passed instead.
|
||||
func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
|
||||
func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix string, pos token.Pos) llvm.Value {
|
||||
var wrapper llvm.Value
|
||||
|
||||
builder := c.ctx.NewBuilder()
|
||||
@@ -206,15 +114,8 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
|
||||
// Create the list of params for the call.
|
||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip parentHandle parameter
|
||||
if !hasContext {
|
||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
||||
}
|
||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
||||
if !hasContext {
|
||||
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
|
||||
}
|
||||
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy parentHandle parameter
|
||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes[:len(paramTypes)-1])
|
||||
params = append(params, llvm.Undef(c.i8ptrType))
|
||||
|
||||
// Create the call.
|
||||
builder.CreateCall(fn, params, "")
|
||||
@@ -276,11 +177,8 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
// Get the function pointer.
|
||||
fnPtr := params[len(params)-1]
|
||||
|
||||
// The last parameter in the packed object has somewhat of a dual role.
|
||||
// Inside the parameter bundle it's the function pointer, stored right
|
||||
// after the context pointer. But in the IR call instruction, it's the
|
||||
// parentHandle function that's always undef outside of the coroutines
|
||||
// scheduler. Thus, make the parameter undef here.
|
||||
// Ignore the last param, which isn't used anymore.
|
||||
// TODO: avoid this extra "parent handle" parameter in most functions.
|
||||
params[len(params)-1] = llvm.Undef(c.i8ptrType)
|
||||
|
||||
// Create the call.
|
||||
|
||||
+2
-13
@@ -311,21 +311,10 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
||||
// used during the interface lowering pass.
|
||||
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
||||
signature := methodSignature(method)
|
||||
var globalName string
|
||||
if token.IsExported(method.Name()) {
|
||||
globalName = "reflect/methods." + signature
|
||||
} else {
|
||||
globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
|
||||
}
|
||||
signatureGlobal := c.mod.NamedGlobal(globalName)
|
||||
signatureGlobal := c.mod.NamedGlobal("func " + signature)
|
||||
if signatureGlobal.IsNil() {
|
||||
// TODO: put something useful in these globals, such as the method
|
||||
// signature. Useful to one day implement reflect.Value.Method(n).
|
||||
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
|
||||
signatureGlobal.SetInitializer(llvm.ConstInt(c.ctx.Int8Type(), 0, false))
|
||||
signatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature)
|
||||
signatureGlobal.SetGlobalConstant(true)
|
||||
signatureGlobal.SetAlignment(1)
|
||||
}
|
||||
return signatureGlobal
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
// type are lowered in the interrupt lowering pass.
|
||||
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
||||
globalLLVMType := b.getLLVMType(globalType)
|
||||
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||
if global := b.mod.NamedGlobal(globalName); !global.IsNil() {
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
||||
}
|
||||
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
global.SetGlobalConstant(true)
|
||||
|
||||
@@ -48,57 +48,3 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
|
||||
// possible. It returns the call result if possible, and a boolean whether it
|
||||
// succeeded. If it doesn't succeed, the architecture doesn't support the given
|
||||
// intrinsic.
|
||||
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
// Check whether this intrinsic is supported on the given GOARCH.
|
||||
// If it is unsupported, this can have two reasons:
|
||||
//
|
||||
// 1. LLVM can expand the intrinsic inline (using float instructions), but
|
||||
// the result doesn't pass the tests of the math package.
|
||||
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
|
||||
// libm function call, but the libm function call also fails the math
|
||||
// package tests.
|
||||
//
|
||||
// Whatever the implementation, it must pass the tests in the math package
|
||||
// so unfortunately only the below intrinsic+architecture combinations are
|
||||
// supported.
|
||||
name := call.StaticCallee().RelString(nil)
|
||||
switch name {
|
||||
case "math.Ceil", "math.Floor", "math.Trunc":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
case "math.Sqrt":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, false // only the above functions are supported.
|
||||
}
|
||||
|
||||
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
|
||||
if llvmFn.IsNil() {
|
||||
// The intrinsic doesn't exist yet, so declare it.
|
||||
// At the moment, all supported intrinsics have the form "double
|
||||
// foo(double %x)" so we can hardcode the signature here.
|
||||
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
|
||||
}
|
||||
// Create a call to the intrinsic.
|
||||
args := make([]llvm.Value, len(call.Args))
|
||||
for i, arg := range call.Args {
|
||||
args[i] = b.getValue(arg)
|
||||
}
|
||||
return b.CreateCall(llvmFn, args, ""), true
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ type functionInfo struct {
|
||||
module string // go:wasm-module
|
||||
importName string // go:linkname, go:export - The name the developer assigns
|
||||
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
|
||||
section string // go:section - object file section name
|
||||
exported bool // go:export, CGo
|
||||
nobounds bool // go:nobounds
|
||||
variadic bool // go:variadic (CGo only)
|
||||
@@ -271,10 +270,6 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.linkName = parts[2]
|
||||
}
|
||||
case "//go:section":
|
||||
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.section = parts[1]
|
||||
}
|
||||
case "//go:nobounds":
|
||||
// Skip bounds checking in this function. Useful for some
|
||||
// runtime functions.
|
||||
@@ -330,7 +325,6 @@ type globalInfo struct {
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
align int // go:align
|
||||
section string // go:section
|
||||
}
|
||||
|
||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||
@@ -444,10 +438,6 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
||||
if err == nil {
|
||||
info.align = align
|
||||
}
|
||||
case "//go:section":
|
||||
if len(parts) == 2 {
|
||||
info.section = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-30
@@ -10,11 +10,11 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createRawSyscall creates a system call with the provided system call number
|
||||
// and returns the result as a single integer (the system call result). The
|
||||
// result is not further interpreted.
|
||||
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
// createSyscall emits an inline system call instruction, depending on the
|
||||
// target OS/arch.
|
||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num := b.getValue(call.Args[0])
|
||||
var syscallResult llvm.Value
|
||||
switch {
|
||||
case b.GOARCH == "amd64":
|
||||
if b.GOOS == "darwin" {
|
||||
@@ -57,7 +57,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
constraints += ",~{rcx},~{r11}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||
// Sources:
|
||||
// syscall(2) man page
|
||||
@@ -83,7 +83,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||
// Implement the EABI system call convention for Linux.
|
||||
// Source: syscall(2) man page.
|
||||
@@ -115,7 +115,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
@@ -147,19 +147,10 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
constraints += ",~{x16},~{x17}" // scratch registers
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
// createSyscall emits instructions for the syscall.Syscall* family of
|
||||
// functions, depending on the target OS/arch.
|
||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
syscallResult, err := b.createRawSyscall(call)
|
||||
if err != nil {
|
||||
return syscallResult, err
|
||||
}
|
||||
switch b.GOOS {
|
||||
case "linux", "freebsd":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
@@ -199,16 +190,3 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
// createRawSyscallNoError emits instructions for the Linux-specific
|
||||
// syscall.rawSyscallNoError function.
|
||||
func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
syscallResult, err := b.createRawSyscall(call)
|
||||
if err != nil {
|
||||
return syscallResult, err
|
||||
}
|
||||
retval := llvm.ConstNull(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false))
|
||||
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = b.CreateInsertValue(retval, llvm.ConstInt(b.uintptrType, 0, false), 1, "")
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
Vendored
-15
@@ -55,18 +55,3 @@ func complexMul(x, y complex64) complex64 {
|
||||
}
|
||||
|
||||
// TODO: complexDiv (requires runtime call)
|
||||
|
||||
// A type 'kv' also exists in function foo. Test that these two types don't
|
||||
// conflict with each other.
|
||||
type kv struct {
|
||||
v float32
|
||||
}
|
||||
|
||||
func foo(a *kv) {
|
||||
// Define a new 'kv' type.
|
||||
type kv struct {
|
||||
v byte
|
||||
}
|
||||
// Use this type.
|
||||
func(b *kv) {}(nil)
|
||||
}
|
||||
|
||||
Vendored
+2
-16
@@ -1,10 +1,7 @@
|
||||
; ModuleID = 'basic.go'
|
||||
source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%main.kv = type { float }
|
||||
%main.kv.0 = type { i8 }
|
||||
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"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
@@ -101,14 +98,3 @@ entry:
|
||||
%7 = insertvalue { float, float } %6, float %5, 1
|
||||
ret { float, float } %7
|
||||
}
|
||||
|
||||
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @"main.foo$1"(%main.kv.0* null, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
-25
@@ -1,25 +0,0 @@
|
||||
package main
|
||||
|
||||
func chanIntSend(ch chan int) {
|
||||
ch <- 3
|
||||
}
|
||||
|
||||
func chanIntRecv(ch chan int) {
|
||||
<-ch
|
||||
}
|
||||
|
||||
func chanZeroSend(ch chan struct{}) {
|
||||
ch <- struct{}{}
|
||||
}
|
||||
|
||||
func chanZeroRecv(ch chan struct{}) {
|
||||
<-ch
|
||||
}
|
||||
|
||||
func selectZeroRecv(ch1 chan int, ch2 chan struct{}) {
|
||||
select {
|
||||
case ch1 <- 1:
|
||||
case <-ch2:
|
||||
default:
|
||||
}
|
||||
}
|
||||
Vendored
-114
@@ -1,114 +0,0 @@
|
||||
; ModuleID = 'channel.go'
|
||||
source_filename = "channel.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||
%"internal/task.state" = type { i8* }
|
||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
store i32 3, i32* %chan.value, align 4
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: argmemonly nounwind willreturn
|
||||
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #0
|
||||
|
||||
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||
|
||||
; Function Attrs: argmemonly nounwind willreturn
|
||||
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #0
|
||||
|
||||
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||
|
||||
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||
%select.send.value = alloca i32, align 4
|
||||
store i32 1, i32* %select.send.value, align 4
|
||||
%select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||
%.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
|
||||
store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
|
||||
%.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
|
||||
%0 = bitcast i8** %.repack1 to i32**
|
||||
store i32* %select.send.value, i32** %0, align 4
|
||||
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
|
||||
store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
|
||||
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
|
||||
store i8* null, i8** %.repack4, align 4
|
||||
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
|
||||
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||
%2 = icmp eq i32 %1, 0
|
||||
br i1 %2, label %select.done, label %select.next
|
||||
|
||||
select.done: ; preds = %select.body, %select.next, %entry
|
||||
ret void
|
||||
|
||||
select.next: ; preds = %entry
|
||||
%3 = icmp eq i32 %1, 1
|
||||
br i1 %3, label %select.body, label %select.done
|
||||
|
||||
select.body: ; preds = %select.next
|
||||
br label %select.done
|
||||
}
|
||||
|
||||
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*, i8*)
|
||||
|
||||
attributes #0 = { argmemonly nounwind willreturn }
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'float.go'
|
||||
source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
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"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'func.go'
|
||||
source_filename = "func.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
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"
|
||||
|
||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||
|
||||
|
||||
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
package main
|
||||
|
||||
// Test changes to the language introduced in Go 1.17.
|
||||
// For details, see: https://tip.golang.org/doc/go1.17#language
|
||||
// These tests should be merged into the regular slice tests once Go 1.17 is the
|
||||
// minimun Go version for TinyGo.
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
|
||||
return unsafe.Add(p, len)
|
||||
}
|
||||
|
||||
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
|
||||
return unsafe.Add(p, len)
|
||||
}
|
||||
|
||||
func SliceToArray(s []int) *[4]int {
|
||||
return (*[4]int)(s)
|
||||
}
|
||||
|
||||
func SliceToArrayConst() *[4]int {
|
||||
s := make([]int, 6)
|
||||
return (*[4]int)(s)
|
||||
}
|
||||
|
||||
func SliceInt(ptr *int, len int) []int {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
|
||||
func SliceUint16(ptr *byte, len uint16) []byte {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
|
||||
func SliceUint64(ptr *int, len uint64) []int {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
|
||||
func SliceInt64(ptr *int, len int64) []int {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
Vendored
-136
@@ -1,136 +0,0 @@
|
||||
; ModuleID = 'go1.17.go'
|
||||
source_filename = "go1.17.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr i8, i8* %p, i32 %len
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = trunc i64 %len to i32
|
||||
%1 = getelementptr i8, i8* %p, i32 %0
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ult i32 %s.len, 4
|
||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
call void @runtime.sliceToArrayPointerPanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slicetoarray.next: ; preds = %entry
|
||||
%1 = bitcast i32* %s.data to [4 x i32]*
|
||||
ret [4 x i32]* %1
|
||||
}
|
||||
|
||||
declare void @runtime.sliceToArrayPointerPanic(i8*, i8*)
|
||||
|
||||
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%makeslice = call i8* @runtime.alloc(i32 24, i8* undef, i8* null)
|
||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
unreachable
|
||||
|
||||
slicetoarray.next: ; preds = %entry
|
||||
%0 = bitcast i8* %makeslice to [4 x i32]*
|
||||
ret [4 x i32]* %0
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ugt i32 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
%2 = icmp ne i32 %len, 0
|
||||
%3 = and i1 %1, %2
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
|
||||
ret { i32*, i32, i32 } %7
|
||||
}
|
||||
|
||||
declare void @runtime.unsafeSlicePanic(i8*, i8*)
|
||||
|
||||
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp eq i8* %ptr, null
|
||||
%1 = icmp ne i16 %len, 0
|
||||
%2 = and i1 %0, %1
|
||||
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%3 = zext i16 %len to i32
|
||||
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
|
||||
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
|
||||
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
|
||||
ret { i8*, i32, i32 } %6
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
%2 = icmp ne i64 %len, 0
|
||||
%3 = and i1 %1, %2
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = trunc i64 %len to i32
|
||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||
ret { i32*, i32, i32 } %8
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
%2 = icmp ne i64 %len, 0
|
||||
%3 = and i1 %1, %2
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = trunc i64 %len to i32
|
||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||
ret { i32*, i32, i32 } %8
|
||||
}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
; ModuleID = 'goroutine.go'
|
||||
source_filename = "goroutine.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7m-none-eabi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||
%"internal/task.state" = type { i32, i32* }
|
||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef, i8* undef)
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, i8*, i8*)
|
||||
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #0 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint i8* %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*, i8*)
|
||||
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef)
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #1 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint i8* %0 to i32
|
||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%n = call i8* @runtime.alloc(i32 4, i8* undef, i8* null)
|
||||
%0 = bitcast i8* %n to i32*
|
||||
store i32 3, i32* %0, align 4
|
||||
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
||||
%2 = bitcast i8* %1 to i32*
|
||||
store i32 5, i32* %2, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
store i8* %n, i8** %4, align 4
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef)
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef, i8* null)
|
||||
%5 = load i32, i32* %0, align 4
|
||||
call void @runtime.printint32(i32 %5, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%unpack.ptr = bitcast i8* %context to i32*
|
||||
store i32 7, i32* %unpack.ptr, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
%2 = load i32, i32* %1, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
%5 = load i8*, i8** %4, align 4
|
||||
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.printint32(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.funcGoroutine(i8* %fn.context, void (i32, i8*, i8*)* %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 12, i8* undef, i8* null)
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
store i32 5, i32* %1, align 4
|
||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
%3 = bitcast i8* %2 to i8**
|
||||
store i8* %fn.context, i8** %3, align 4
|
||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%5 = bitcast i8* %4 to void (i32, i8*, i8*)**
|
||||
store void (i32, i8*, i8*)* %fn.funcptr, void (i32, i8*, i8*)** %5, align 4
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef, i8* undef)
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
%2 = load i32, i32* %1, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
%5 = load i8*, i8** %4, align 4
|
||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%7 = bitcast i8* %6 to void (i32, i8*, i8*)**
|
||||
%8 = load void (i32, i8*, i8*)*, void (i32, i8*, i8*)** %7, align 4
|
||||
call void %8(i32 %2, i8* %5, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||
|
||||
attributes #0 = { "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #1 = { "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #2 = { "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #3 = { "tinygo-gowrapper" }
|
||||
Vendored
-106
@@ -1,106 +0,0 @@
|
||||
; ModuleID = 'goroutine.go'
|
||||
source_filename = "goroutine.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||
%"internal/task.state" = type { i8* }
|
||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||
|
||||
@"main.regularFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
||||
@"main.inlineFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
||||
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
||||
@"main.closureFunctionGoroutine$1$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @main.regularFunction to i32), i8* bitcast ({ i32, i8* }* @"main.regularFunctionGoroutine$pack" to i8*), i32 undef, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, i8*, i8*)
|
||||
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.inlineFunctionGoroutine$1" to i32), i8* bitcast ({ i32, i8* }* @"main.inlineFunctionGoroutine$pack" to i8*), i32 undef, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%n = call i8* @runtime.alloc(i32 4, i8* undef, i8* null)
|
||||
%0 = bitcast i8* %n to i32*
|
||||
store i32 3, i32* %0, align 4
|
||||
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
||||
%2 = bitcast i8* %1 to i32*
|
||||
store i32 5, i32* %2, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
store i8* %n, i8** %4, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* nonnull %1, i32 undef, i8* undef, i8* null)
|
||||
%5 = load i32, i32* %0, align 4
|
||||
call void @runtime.printint32(i32 %5, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%unpack.ptr = bitcast i8* %context to i32*
|
||||
store i32 7, i32* %unpack.ptr, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.printint32(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.funcGoroutine(i8* %fn.context, i32 %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i32 @runtime.getFuncPtr(i8* %fn.context, i32 %fn.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null)
|
||||
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
||||
%2 = bitcast i8* %1 to i32*
|
||||
store i32 5, i32* %2, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
store i8* %fn.context, i8** %4, align 4
|
||||
call void @"internal/task.start"(i32 %0, i8* nonnull %1, i32 undef, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||
|
||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||
Vendored
-43
@@ -1,43 +0,0 @@
|
||||
package main
|
||||
|
||||
func regularFunctionGoroutine() {
|
||||
go regularFunction(5)
|
||||
}
|
||||
|
||||
func inlineFunctionGoroutine() {
|
||||
go func(x int) {
|
||||
}(5)
|
||||
}
|
||||
|
||||
func closureFunctionGoroutine() {
|
||||
n := 3
|
||||
go func(x int) {
|
||||
n = 7
|
||||
}(5)
|
||||
print(n) // note: this is racy (but good enough for this test)
|
||||
}
|
||||
|
||||
func funcGoroutine(fn func(x int)) {
|
||||
go fn(5)
|
||||
}
|
||||
|
||||
func recoverBuiltinGoroutine() {
|
||||
// This is a no-op.
|
||||
go recover()
|
||||
}
|
||||
|
||||
func copyBuiltinGoroutine(dst, src []byte) {
|
||||
// This is not run in a goroutine. While this copy operation can indeed take
|
||||
// some time (if there is a lot of data to copy), there is no race-free way
|
||||
// to make use of the result so it's unlikely applications will make use of
|
||||
// it. And doing it this way should be just within the Go specification.
|
||||
go copy(dst, src)
|
||||
}
|
||||
|
||||
func closeBuiltinGoroutine(ch chan int) {
|
||||
// This builtin is executed directly, not in a goroutine.
|
||||
// The observed behavior is the same.
|
||||
go close(ch)
|
||||
}
|
||||
|
||||
func regularFunction(x int)
|
||||
Vendored
+8
-8
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'interface.go'
|
||||
source_filename = "interface.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
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"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||
@@ -13,15 +13,15 @@ target triple = "wasm32--wasi"
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error" }
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }
|
||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
||||
@"func Error() string" = external constant i8
|
||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }
|
||||
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"]
|
||||
@"func String() string" = external constant i8
|
||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
@@ -92,7 +92,7 @@ typeassert.next: ; preds = %typeassert.ok, %ent
|
||||
|
||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Error() string", i8* undef, i8* null)
|
||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"func Error() string", i8* undef, i8* null)
|
||||
%invoke.func.cast = inttoptr i32 %invoke.func to %runtime._string (i8*, i8*, i8*)*
|
||||
%0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef)
|
||||
ret %runtime._string %0
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
; ModuleID = 'intrinsics.go'
|
||||
source_filename = "intrinsics.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7m-none-eabi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @math.Sqrt(double %x, i8* undef, i8* undef)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
declare double @math.Sqrt(double, i8*, i8*)
|
||||
|
||||
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @math.Trunc(double %x, i8* undef, i8* undef)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
declare double @math.Trunc(double, i8*, i8*)
|
||||
Vendored
-31
@@ -1,31 +0,0 @@
|
||||
; ModuleID = 'intrinsics.go'
|
||||
source_filename = "intrinsics.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @llvm.sqrt.f64(double %x)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone speculatable willreturn
|
||||
declare double @llvm.sqrt.f64(double) #0
|
||||
|
||||
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @llvm.trunc.f64(double %x)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone speculatable willreturn
|
||||
declare double @llvm.trunc.f64(double) #0
|
||||
|
||||
attributes #0 = { nounwind readnone speculatable willreturn }
|
||||
Vendored
-14
@@ -1,14 +0,0 @@
|
||||
package main
|
||||
|
||||
// Test how intrinsics are lowered: either as regular calls to the math
|
||||
// functions or as LLVM builtins (such as llvm.sqrt.f64).
|
||||
|
||||
import "math"
|
||||
|
||||
func mySqrt(x float64) float64 {
|
||||
return math.Sqrt(x)
|
||||
}
|
||||
|
||||
func myTrunc(x float64) float64 {
|
||||
return math.Trunc(x)
|
||||
}
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'pointer.go'
|
||||
source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
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"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
-66
@@ -1,66 +0,0 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// Creates an external global with name extern_global.
|
||||
//go:extern extern_global
|
||||
var externGlobal [0]byte
|
||||
|
||||
// Creates a
|
||||
//go:align 32
|
||||
var alignedGlobal [4]uint32
|
||||
|
||||
// Test conflicting pragmas (the last one counts).
|
||||
//go:align 64
|
||||
//go:align 16
|
||||
var alignedGlobal16 [4]uint32
|
||||
|
||||
// Test exported functions.
|
||||
//export extern_func
|
||||
func externFunc() {
|
||||
}
|
||||
|
||||
// Define a function in a different package using go:linkname.
|
||||
//go:linkname withLinkageName1 somepkg.someFunction1
|
||||
func withLinkageName1() {
|
||||
}
|
||||
|
||||
// Import a function from a different package using go:linkname.
|
||||
//go:linkname withLinkageName2 somepkg.someFunction2
|
||||
func withLinkageName2()
|
||||
|
||||
// Function has an 'inline hint', similar to the inline keyword in C.
|
||||
//go:inline
|
||||
func inlineFunc() {
|
||||
}
|
||||
|
||||
// Function should never be inlined, equivalent to GCC
|
||||
// __attribute__((noinline)).
|
||||
//go:noinline
|
||||
func noinlineFunc() {
|
||||
}
|
||||
|
||||
// This function should have the specified section.
|
||||
//go:section .special_function_section
|
||||
func functionInSection() {
|
||||
}
|
||||
|
||||
//export exportedFunctionInSection
|
||||
//go:section .special_function_section
|
||||
func exportedFunctionInSection() {
|
||||
}
|
||||
|
||||
// This function should not: it's only a declaration and not a definition.
|
||||
//go:section .special_function_section
|
||||
func undefinedFunctionNotInSection()
|
||||
|
||||
//go:section .special_global_section
|
||||
var globalInSection uint32
|
||||
|
||||
//go:section .special_global_section
|
||||
//go:extern undefinedGlobalNotInSection
|
||||
var undefinedGlobalNotInSection uint32
|
||||
|
||||
//go:align 1024
|
||||
//go:section .global_section
|
||||
var multipleGlobalPragmas uint32
|
||||
Vendored
-59
@@ -1,59 +0,0 @@
|
||||
; ModuleID = 'pragma.go'
|
||||
source_filename = "pragma.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
@extern_global = external global [0 x i8], align 1
|
||||
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
||||
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
||||
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
||||
@undefinedGlobalNotInSection = external global i32, align 4
|
||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @extern_func() #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @somepkg.someFunction1(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @somepkg.someFunction2(i8*, i8*)
|
||||
|
||||
; Function Attrs: inlinehint
|
||||
define hidden void @main.inlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline
|
||||
define hidden void @main.noinlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.functionInSection(i8* %context, i8* %parentHandle) unnamed_addr section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @exportedFunctionInSection() #3 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.undefinedFunctionNotInSection(i8*, i8*)
|
||||
|
||||
attributes #0 = { "wasm-export-name"="extern_func" }
|
||||
attributes #1 = { inlinehint }
|
||||
attributes #2 = { noinline }
|
||||
attributes #3 = { "wasm-export-name"="exportedFunctionInSection" }
|
||||
Vendored
-18
@@ -23,21 +23,3 @@ func sliceAppendSlice(ints, added []int) []int {
|
||||
func sliceCopy(dst, src []int) int {
|
||||
return copy(dst, src)
|
||||
}
|
||||
|
||||
// Test bounds checking in *ssa.MakeSlice instruction.
|
||||
|
||||
func makeByteSlice(len int) []byte {
|
||||
return make([]byte, len)
|
||||
}
|
||||
|
||||
func makeInt16Slice(len int) []int16 {
|
||||
return make([]int16, len)
|
||||
}
|
||||
|
||||
func makeArraySlice(len int) [][3]byte {
|
||||
return make([][3]byte, len) // slice with element size of 3
|
||||
}
|
||||
|
||||
func makeInt32Slice(len int) []int32 {
|
||||
return make([]int32, len)
|
||||
}
|
||||
|
||||
Vendored
+2
-78
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'slice.go'
|
||||
source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
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"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
@@ -86,79 +86,3 @@ entry:
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* undef, i8* null)
|
||||
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
||||
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
||||
ret { i8*, i32, i32 } %2
|
||||
}
|
||||
|
||||
declare void @runtime.slicePanic(i8*, i8*)
|
||||
|
||||
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = shl i32 %len, 1
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
||||
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
||||
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
||||
ret { i16*, i32, i32 } %2
|
||||
}
|
||||
|
||||
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = mul i32 %len, 3
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
||||
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
|
||||
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
||||
ret { [3 x i8]*, i32, i32 } %2
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = shl i32 %len, 2
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
||||
ret { i32*, i32, i32 } %2
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'string.go'
|
||||
source_filename = "string.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
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"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.15
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
@@ -10,7 +10,7 @@ require (
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
go.bug.st/serial v1.1.2
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
||||
)
|
||||
|
||||
@@ -30,8 +30,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
|
||||
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
|
||||
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
|
||||
@@ -40,32 +38,23 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||
|
||||
+5
-5
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.20.0"
|
||||
const Version = "0.18.0-dev"
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
@@ -48,10 +48,7 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
|
||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||
// can have some variations (for beta releases, for example).
|
||||
func GorootVersionString(goroot string) (string, error) {
|
||||
if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(
|
||||
if data, err := ioutil.ReadFile(filepath.Join(
|
||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||
@@ -62,6 +59,9 @@ func GorootVersionString(goroot string) (string, error) {
|
||||
|
||||
return string(matches[1]), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
|
||||
pos := getPosition(inst.llvmInst)
|
||||
return &Error{
|
||||
ImportPath: r.pkgName,
|
||||
Inst: inst.llvmInst,
|
||||
Pos: pos,
|
||||
Err: err,
|
||||
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
||||
|
||||
+3
-34
@@ -15,7 +15,7 @@ import (
|
||||
// package is changed in a way that affects the output so that cached package
|
||||
// builds will be invalidated.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 2 // last change: fix GEP on untyped pointers
|
||||
const Version = 1
|
||||
|
||||
// Enable extra checks, which should be disabled by default.
|
||||
// This may help track down bugs by adding a few more sanity checks.
|
||||
@@ -110,26 +110,17 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
||||
}
|
||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||
call.EraseFromParentAsInstruction()
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
||||
}
|
||||
// Remove instructions that were created as part of interpreting
|
||||
// the package.
|
||||
mem.revert()
|
||||
// Create a call to the package initializer (which was
|
||||
// previously deleted).
|
||||
i8undef := llvm.Undef(r.i8ptrType)
|
||||
r.builder.CreateCall(fn, []llvm.Value{i8undef, i8undef}, "")
|
||||
// Make sure that any globals touched by the package
|
||||
// initializer, won't be accessed by later package initializers.
|
||||
r.markExternalLoad(fn)
|
||||
continue
|
||||
}
|
||||
return callErr
|
||||
}
|
||||
call.EraseFromParentAsInstruction()
|
||||
for index, obj := range mem.objects {
|
||||
r.objects[index] = obj
|
||||
}
|
||||
@@ -138,7 +129,7 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
|
||||
// Update all global variables in the LLVM module.
|
||||
mem := memoryView{r: r}
|
||||
for i, obj := range r.objects {
|
||||
for _, obj := range r.objects {
|
||||
if obj.llvmGlobal.IsNil() {
|
||||
continue
|
||||
}
|
||||
@@ -168,12 +159,6 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
name := obj.llvmGlobal.Name()
|
||||
obj.llvmGlobal.EraseFromParentAsGlobal()
|
||||
newGlobal.SetName(name)
|
||||
|
||||
// Update interp-internal references.
|
||||
delete(r.globals, obj.llvmGlobal)
|
||||
obj.llvmGlobal = newGlobal
|
||||
r.globals[newGlobal] = i
|
||||
r.objects[i] = obj
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
@@ -279,19 +264,3 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||
r.functionCache[llvmFn] = fn
|
||||
return fn
|
||||
}
|
||||
|
||||
// markExternalLoad marks the given llvmValue as being loaded externally. This
|
||||
// is primarily used to mark package initializers that could not be run at
|
||||
// compile time. As an example, a package initialize might store to a global
|
||||
// variable. Another package initializer might read from the same global
|
||||
// variable. By marking this function as being run at runtime, that load
|
||||
// instruction will need to be run at runtime instead of at compile time.
|
||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
||||
mem := memoryView{r: r}
|
||||
mem.markExternalLoad(llvmValue)
|
||||
for index, obj := range mem.objects {
|
||||
if obj.marked > r.objects[index].marked {
|
||||
r.objects[index].marked = obj.marked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ func TestInterp(t *testing.T) {
|
||||
"slice-copy",
|
||||
"consteval",
|
||||
"interface",
|
||||
"revert",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
+10
-22
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -590,14 +589,18 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -927,31 +930,16 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
|
||||
case llvm.ExtractValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
// Note: the Go LLVM API doesn't support multiple indices, so simulate
|
||||
// this operation with some extra extractvalue instructions. Hopefully
|
||||
// this is optimized to a single instruction.
|
||||
agg := operands[0]
|
||||
for i := 0; i < len(indices)-1; i++ {
|
||||
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
}
|
||||
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
|
||||
result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
|
||||
case llvm.InsertValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
// Similar to extractvalue, we're working around a limitation in the Go
|
||||
// LLVM API here by splitting the insertvalue into multiple instructions
|
||||
// if there is more than one operand.
|
||||
agg := operands[0]
|
||||
aggregates := []llvm.Value{agg}
|
||||
for i := 0; i < len(indices)-1; i++ {
|
||||
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
|
||||
aggregates = append(aggregates, agg)
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
}
|
||||
result = operands[1]
|
||||
for i := len(indices) - 1; i >= 0; i-- {
|
||||
agg := aggregates[i]
|
||||
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
@@ -178,9 +178,6 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
||||
default:
|
||||
panic("interp: unknown constant expression")
|
||||
}
|
||||
} else if !llvmValue.IsAInlineAsm().IsNil() {
|
||||
// Inline assembly can modify globals but only exported globals. Let's
|
||||
// hope the author knows what they're doing.
|
||||
} else {
|
||||
llvmType := llvmValue.Type()
|
||||
switch llvmType.TypeKind() {
|
||||
@@ -572,17 +569,6 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
||||
}
|
||||
|
||||
if llvmType.IsNil() {
|
||||
if v.offset() != 0 {
|
||||
// If there is an offset, make sure to use a GEP to index into the
|
||||
// pointer. Because there is no expected type, we use whatever is
|
||||
// most convenient: an *i8 type. It is trivial to index byte-wise.
|
||||
if llvmValue.Type() != mem.r.i8ptrType {
|
||||
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
|
||||
}
|
||||
llvmValue = llvm.ConstInBoundsGEP(llvmValue, []llvm.Value{
|
||||
llvm.ConstInt(llvmValue.Type().Context().Int32Type(), uint64(v.offset()), false),
|
||||
})
|
||||
}
|
||||
return llvmValue, nil
|
||||
}
|
||||
|
||||
|
||||
Vendored
-22
@@ -8,7 +8,6 @@ target triple = "x86_64--linux"
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = global i16 0
|
||||
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -66,22 +65,12 @@ entry:
|
||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||
store i16 7, i16* @main.exposedValue2
|
||||
|
||||
; Test that inline assembly is ignored.
|
||||
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
|
||||
|
||||
; Test switch statement.
|
||||
%switch1 = call i64 @testSwitch(i64 1) ; 1 returns 6
|
||||
%switch2 = call i64 @testSwitch(i64 9) ; 9 returns the default value -1
|
||||
call void @runtime.printint64(i64 %switch1)
|
||||
call void @runtime.printint64(i64 %switch2)
|
||||
|
||||
; Test extractvalue/insertvalue with multiple operands.
|
||||
%agg = call {i8, i32, {float, {i64, i16}}} @nestedStruct()
|
||||
%elt = extractvalue {i8, i32, {float, {i64, i16}}} %agg, 2, 1, 0
|
||||
call void @runtime.printint64(i64 %elt)
|
||||
%agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0
|
||||
store {i8, i32, {float, {i64, i16}}} %agg2, {i8, i32, {float, {i64, i16}}}* @main.insertedValue
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -105,15 +94,6 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Inline assembly should be ignored in the interp package. While it is possible
|
||||
; to modify other globals that way, usually that's not the case and there is no
|
||||
; real way to check.
|
||||
define void @hasInlineAsm() {
|
||||
entry:
|
||||
call void asm sideeffect "", ""()
|
||||
ret void
|
||||
}
|
||||
|
||||
define i64 @testSwitch(i64 %val) {
|
||||
entry:
|
||||
; Test switch statement.
|
||||
@@ -132,5 +112,3 @@ two:
|
||||
otherwise:
|
||||
ret i64 -1
|
||||
}
|
||||
|
||||
declare {i8, i32, {float, {i64, i16}}} @nestedStruct()
|
||||
|
||||
Vendored
-21
@@ -7,7 +7,6 @@ target triple = "x86_64--linux"
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = local_unnamed_addr global i16 0
|
||||
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -26,20 +25,8 @@ entry:
|
||||
store i16 5, i16* @main.exposedValue1
|
||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||
store i16 7, i16* @main.exposedValue2
|
||||
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
|
||||
call void @runtime.printint64(i64 6)
|
||||
call void @runtime.printint64(i64 -1)
|
||||
%agg = call { i8, i32, { float, { i64, i16 } } } @nestedStruct()
|
||||
%elt.agg = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
|
||||
%elt.agg1 = extractvalue { float, { i64, i16 } } %elt.agg, 1
|
||||
%elt = extractvalue { i64, i16 } %elt.agg1, 0
|
||||
call void @runtime.printint64(i64 %elt)
|
||||
%agg2.agg0 = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
|
||||
%agg2.agg1 = extractvalue { float, { i64, i16 } } %agg2.agg0, 1
|
||||
%agg2.insertvalue2 = insertvalue { i64, i16 } %agg2.agg1, i64 5, 0
|
||||
%agg2.insertvalue1 = insertvalue { float, { i64, i16 } } %agg2.agg0, { i64, i16 } %agg2.insertvalue2, 1
|
||||
%agg2.insertvalue0 = insertvalue { i8, i32, { float, { i64, i16 } } } %agg, { float, { i64, i16 } } %agg2.insertvalue1, 2
|
||||
store { i8, i32, { float, { i64, i16 } } } %agg2.insertvalue0, { i8, i32, { float, { i64, i16 } } }* @main.insertedValue
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -60,12 +47,6 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @hasInlineAsm() {
|
||||
entry:
|
||||
call void asm sideeffect "", ""()
|
||||
ret void
|
||||
}
|
||||
|
||||
define i64 @testSwitch(i64 %val) local_unnamed_addr {
|
||||
entry:
|
||||
switch i64 %val, label %otherwise [
|
||||
@@ -86,5 +67,3 @@ two: ; preds = %entry
|
||||
otherwise: ; preds = %entry
|
||||
ret i64 -1
|
||||
}
|
||||
|
||||
declare { i8, i32, { float, { i64, i16 } } } @nestedStruct() local_unnamed_addr
|
||||
|
||||
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
declare void @externalCall(i64)
|
||||
|
||||
@foo.knownAtRuntime = global i64 0
|
||||
@bar.knownAtRuntime = global i64 0
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @foo.init(i8* undef, i8* undef)
|
||||
call void @bar.init(i8* undef, i8* undef)
|
||||
call void @main.init(i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
store i64 5, i64* @foo.knownAtRuntime
|
||||
unreachable ; this triggers a revert of @foo.init.
|
||||
}
|
||||
|
||||
define internal void @bar.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
%val = load i64, i64* @foo.knownAtRuntime
|
||||
store i64 %val, i64* @bar.knownAtRuntime
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @externalCall(i64 3)
|
||||
ret void
|
||||
}
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@foo.knownAtRuntime = local_unnamed_addr global i64 0
|
||||
@bar.knownAtRuntime = local_unnamed_addr global i64 0
|
||||
|
||||
declare void @externalCall(i64) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call fastcc void @foo.init(i8* undef, i8* undef)
|
||||
%val = load i64, i64* @foo.knownAtRuntime, align 8
|
||||
store i64 %val, i64* @bar.knownAtRuntime, align 8
|
||||
call void @externalCall(i64 3)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
store i64 5, i64* @foo.knownAtRuntime, align 8
|
||||
unreachable
|
||||
}
|
||||
+1
-1
Submodule lib/cmsis-svd updated: df75ff974c...2fb66c5735
@@ -23,13 +23,3 @@ type Error struct {
|
||||
func (e Error) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Error returned when loading a *Program for a test binary but no test files
|
||||
// are present.
|
||||
type NoTestFilesError struct {
|
||||
ImportPath string
|
||||
}
|
||||
|
||||
func (e NoTestFilesError) Error() string {
|
||||
return "no test files"
|
||||
}
|
||||
|
||||
+16
-38
@@ -2,14 +2,6 @@ package loader
|
||||
|
||||
// This file constructs a new temporary GOROOT directory by merging both the
|
||||
// standard Go GOROOT and the GOROOT from TinyGo using symlinks.
|
||||
//
|
||||
// The goal is to replace specific packages from Go with a TinyGo version. It's
|
||||
// never a partial replacement, either a package is fully replaced or it is not.
|
||||
// This is important because if we did allow to merge packages (e.g. by adding
|
||||
// files to a package), it would lead to a dependency on implementation details
|
||||
// with all the maintenance burden that results in. Only allowing to replace
|
||||
// packages as a whole avoids this as packages are already designed to have a
|
||||
// public (backwards-compatible) API.
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
@@ -147,7 +139,6 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hasTinyGoFiles := false
|
||||
for _, e := range tinygoEntries {
|
||||
if e.IsDir() {
|
||||
// A directory, so merge this thing.
|
||||
@@ -163,7 +154,6 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hasTinyGoFiles = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,30 +164,21 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
|
||||
return err
|
||||
}
|
||||
for _, e := range gorootEntries {
|
||||
if e.IsDir() {
|
||||
if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok {
|
||||
// Already included above, so don't bother trying to create this
|
||||
// symlink.
|
||||
continue
|
||||
}
|
||||
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
||||
oldname := filepath.Join(goroot, "src", importPath, e.Name())
|
||||
err := symlink(oldname, newname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Only merge files from Go if TinyGo does not have any files.
|
||||
// Otherwise we'd end up with a weird mix from both Go
|
||||
// implementations.
|
||||
if !hasTinyGoFiles {
|
||||
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
||||
oldname := filepath.Join(goroot, "src", importPath, e.Name())
|
||||
err := symlink(oldname, newname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !e.IsDir() {
|
||||
// Don't merge in files from Go. Otherwise we'd end up with a
|
||||
// weird syscall package with files from both roots.
|
||||
continue
|
||||
}
|
||||
if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok {
|
||||
// Already included above, so don't bother trying to create this
|
||||
// symlink.
|
||||
continue
|
||||
}
|
||||
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
||||
oldname := filepath.Join(goroot, "src", importPath, e.Name())
|
||||
err := symlink(oldname, newname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,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" || tag == "tinygo.wasm" {
|
||||
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -220,8 +201,6 @@ func needsSyscallPackage(buildTags []string) bool {
|
||||
func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
||||
paths := map[string]bool{
|
||||
"/": true,
|
||||
"crypto/": true,
|
||||
"crypto/rand/": false,
|
||||
"device/": false,
|
||||
"examples/": false,
|
||||
"internal/": true,
|
||||
@@ -229,7 +208,6 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
||||
"internal/reflectlite/": false,
|
||||
"internal/task/": false,
|
||||
"machine/": false,
|
||||
"net/": true,
|
||||
"os/": true,
|
||||
"reflect/": false,
|
||||
"runtime/": false,
|
||||
|
||||
+7
-16
@@ -210,12 +210,6 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
p.Packages[pkg.ImportPath] = pkg
|
||||
}
|
||||
|
||||
if config.TestConfig.CompileTestBinary && !strings.HasSuffix(p.sorted[len(p.sorted)-1].ImportPath, ".test") {
|
||||
// Trying to compile a test binary but there are no test files in this
|
||||
// package.
|
||||
return p, NoTestFilesError{p.sorted[len(p.sorted)-1].ImportPath}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@@ -343,11 +337,12 @@ func (p *Package) Check() error {
|
||||
checker.Importer = p
|
||||
|
||||
packageName := p.ImportPath
|
||||
if p == p.program.MainPkg() {
|
||||
if p.Name != "main" {
|
||||
// Sanity check. Should not ever trigger.
|
||||
panic("expected main package to have name 'main'")
|
||||
}
|
||||
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)
|
||||
@@ -386,11 +381,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
}
|
||||
|
||||
// Do CGo processing.
|
||||
// This is done when there are any CgoFiles at all. In that case, len(files)
|
||||
// should be non-zero. However, if len(GoFiles) == 0 and len(CgoFiles) == 1
|
||||
// and there is a syntax error in a CGo file, len(files) may be 0. Don't try
|
||||
// to call cgo.Process in that case as it will only cause issues.
|
||||
if len(p.CgoFiles) != 0 && len(files) != 0 {
|
||||
if len(p.CgoFiles) != 0 {
|
||||
var initialCFlags []string
|
||||
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
|
||||
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||
|
||||
@@ -15,8 +15,6 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -97,31 +95,12 @@ func copyFile(src, dst string) error {
|
||||
|
||||
// executeCommand is a simple wrapper to exec.Cmd
|
||||
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
|
||||
if options.PrintCommands != nil {
|
||||
options.PrintCommands(name, arg...)
|
||||
if options.PrintCommands {
|
||||
fmt.Printf("%s %s\n", name, strings.Join(arg, " "))
|
||||
}
|
||||
return exec.Command(name, arg...)
|
||||
}
|
||||
|
||||
// printCommand prints a command to stdout while formatting it like a real
|
||||
// command (escaping characters etc). The resulting command should be easy to
|
||||
// run directly in a shell, although it is not guaranteed to be a safe shell
|
||||
// escape. That's not a problem as the primary use case is printing the command,
|
||||
// not running it.
|
||||
func printCommand(cmd string, args ...string) {
|
||||
command := append([]string{cmd}, args...)
|
||||
for i, arg := range command {
|
||||
// Source: https://www.oreilly.com/library/view/learning-the-bash/1565923472/ch01s09.html
|
||||
const specialChars = "~`#$&*()\\|[]{};'\"<>?! "
|
||||
if strings.ContainsAny(arg, specialChars) {
|
||||
// See: https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
|
||||
arg = "'" + strings.ReplaceAll(arg, `'`, `'\''`) + "'"
|
||||
command[i] = arg
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, strings.Join(command, " "))
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -157,17 +136,15 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
})
|
||||
}
|
||||
|
||||
// Test runs the tests in the given package. Returns whether the test passed and
|
||||
// possibly an error if the test failed to run.
|
||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVerbose bool, outpath string) (bool, error) {
|
||||
// Test runs the tests in the given package.
|
||||
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 false, err
|
||||
return err
|
||||
}
|
||||
|
||||
passed := true
|
||||
err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
if testCompileOnly || outpath != "" {
|
||||
// Write test binary to the specified file name.
|
||||
if outpath == "" {
|
||||
@@ -181,83 +158,48 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVer
|
||||
// Do not run the test.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
start := time.Now()
|
||||
var err error
|
||||
passed, err = runPackageTest(config, result, testVerbose)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
duration := time.Since(start)
|
||||
|
||||
// Print the result.
|
||||
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||
if passed {
|
||||
fmt.Printf("ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
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 {
|
||||
os.Exit(err.ExitCode())
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
fmt.Printf("FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
// 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 nil
|
||||
})
|
||||
if err, ok := err.(loader.NoTestFilesError); ok {
|
||||
fmt.Printf("? \t%s\t[no test files]\n", err.ImportPath)
|
||||
// Pretend the test passed - it at least didn't fail.
|
||||
return true, nil
|
||||
}
|
||||
return passed, err
|
||||
}
|
||||
|
||||
// runPackageTest runs a test binary that was previously built. The return
|
||||
// values are whether the test passed and any errors encountered while trying to
|
||||
// run the binary.
|
||||
func runPackageTest(config *compileopts.Config, result builder.BuildResult, testVerbose bool) (bool, error) {
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
var flags []string
|
||||
if testVerbose {
|
||||
flags = append(flags, "-test.v")
|
||||
}
|
||||
cmd := executeCommand(config.Options, result.Binary, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = result.MainDir
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
// Binary exited with a non-zero exit code, which means the test
|
||||
// failed.
|
||||
return false, nil
|
||||
}
|
||||
return false, &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return true, nil
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
// TODO: pass the -test.v flag if needed.
|
||||
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 false, &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 true, nil
|
||||
} else {
|
||||
// Test failed, either by ending with the word "FAIL" or with a
|
||||
// panic of some sort.
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flash builds and flashes the built binary to the given serial port.
|
||||
@@ -282,8 +224,6 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
fileExt = ".bin"
|
||||
case strings.Contains(config.Target.FlashCommand, "{uf2}"):
|
||||
fileExt = ".uf2"
|
||||
case strings.Contains(config.Target.FlashCommand, "{zip}"):
|
||||
fileExt = ".zip"
|
||||
default:
|
||||
return errors.New("invalid target file - did you forget the {hex} token in the 'flash-command' section?")
|
||||
}
|
||||
@@ -294,8 +234,6 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
fileExt = filepath.Ext(config.Target.FlashFilename)
|
||||
case "openocd":
|
||||
fileExt = ".hex"
|
||||
case "bmp":
|
||||
fileExt = ".elf"
|
||||
case "native":
|
||||
return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
|
||||
default:
|
||||
@@ -305,7 +243,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||
// do we need port reset to put MCU into bootloader mode?
|
||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||
port, err := getDefaultPort(port, config.Target.SerialPort)
|
||||
port, err := getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -323,36 +261,36 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
case "", "command":
|
||||
// Create the command.
|
||||
flashCmd := config.Target.FlashCommand
|
||||
flashCmdList, err := shlex.Split(flashCmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse flash command %#v: %w", flashCmd, err)
|
||||
}
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
|
||||
|
||||
if strings.Contains(flashCmd, "{port}") {
|
||||
var err error
|
||||
port, err = getDefaultPort(port, config.Target.SerialPort)
|
||||
port, err = getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in fields in the command template.
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
for i, arg := range flashCmdList {
|
||||
arg = strings.ReplaceAll(arg, fileToken, result.Binary)
|
||||
arg = strings.ReplaceAll(arg, "{port}", port)
|
||||
flashCmdList[i] = arg
|
||||
}
|
||||
flashCmd = strings.ReplaceAll(flashCmd, "{port}", port)
|
||||
|
||||
// Execute the command.
|
||||
if len(flashCmdList) < 2 {
|
||||
return fmt.Errorf("invalid flash command: %#v", flashCmd)
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
command := strings.Split(flashCmd, " ")
|
||||
if len(command) < 2 {
|
||||
return errors.New("invalid flash command")
|
||||
}
|
||||
cmd = executeCommand(config.Options, command[0], command[1:]...)
|
||||
default:
|
||||
cmd = executeCommand(config.Options, "/bin/sh", "-c", flashCmd)
|
||||
}
|
||||
cmd := executeCommand(config.Options, flashCmdList[0], flashCmdList[1:]...)
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||
err = cmd.Run()
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
@@ -388,25 +326,6 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
case "bmp":
|
||||
gdb, err := config.Target.LookupGDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var bmpGDBPort string
|
||||
bmpGDBPort, _, err = getBMPPorts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{"-ex", "target extended-remote " + bmpGDBPort, "-ex", "monitor swdp_scan", "-ex", "attach 1", "-ex", "load", filepath.ToSlash(result.Binary)}
|
||||
cmd := executeCommand(config.Options, gdb, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||
}
|
||||
@@ -461,15 +380,8 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
switch gdbInterface {
|
||||
case "native":
|
||||
// Run GDB directly.
|
||||
case "bmp":
|
||||
var bmpGDBPort string
|
||||
bmpGDBPort, _, err = getBMPPorts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gdbCommands = append(gdbCommands, "target extended-remote "+bmpGDBPort, "monitor swdp_scan", "compare-sections", "attach 1", "load")
|
||||
case "openocd":
|
||||
gdbCommands = append(gdbCommands, "target extended-remote :3333", "monitor halt", "load", "monitor reset halt")
|
||||
gdbCommands = append(gdbCommands, "target remote :3333", "monitor halt", "load", "monitor reset halt")
|
||||
|
||||
// We need a separate debugging daemon for on-chip debugging.
|
||||
args, err := config.OpenOCDConfiguration()
|
||||
@@ -488,7 +400,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
daemon.Stderr = w
|
||||
}
|
||||
case "jlink":
|
||||
gdbCommands = append(gdbCommands, "target extended-remote :2331", "load", "monitor reset halt")
|
||||
gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt")
|
||||
|
||||
// We need a separate debugging daemon for on-chip debugging.
|
||||
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
|
||||
@@ -503,7 +415,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
daemon.Stderr = w
|
||||
}
|
||||
case "qemu":
|
||||
gdbCommands = append(gdbCommands, "target extended-remote :1234")
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
|
||||
@@ -511,7 +423,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "qemu-user":
|
||||
gdbCommands = append(gdbCommands, "target extended-remote :1234")
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary)
|
||||
@@ -519,7 +431,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "mgba":
|
||||
gdbCommands = append(gdbCommands, "target extended-remote :2345")
|
||||
gdbCommands = append(gdbCommands, "target remote :2345")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary, "-g")
|
||||
@@ -527,7 +439,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "simavr":
|
||||
gdbCommands = append(gdbCommands, "target extended-remote :1234")
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], "-g", result.Binary)
|
||||
@@ -753,8 +665,7 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err error) {
|
||||
portCandidates := strings.FieldsFunc(portFlag, func(c rune) bool { return c == ',' })
|
||||
func getDefaultPort(portCandidates []string) (port string, err error) {
|
||||
if len(portCandidates) == 1 {
|
||||
return portCandidates[0], nil
|
||||
}
|
||||
@@ -770,70 +681,11 @@ func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err e
|
||||
return "", err
|
||||
}
|
||||
|
||||
var preferredPortIDs [][2]uint16
|
||||
for _, s := range usbInterfaces {
|
||||
parts := strings.Split(s, ":")
|
||||
if len(parts) != 3 || (parts[0] != "acm" && parts[0] == "usb") {
|
||||
// acm and usb are the two types of serial ports recognized
|
||||
// under Linux (ttyACM*, ttyUSB*). Other operating systems don't
|
||||
// generally make this distinction. If this is not one of the
|
||||
// given USB devices, don't try to parse the USB IDs.
|
||||
continue
|
||||
}
|
||||
vid, err := strconv.ParseUint(parts[1], 16, 16)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not parse USB vendor ID %q: %w", parts[1], err)
|
||||
}
|
||||
pid, err := strconv.ParseUint(parts[2], 16, 16)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not parse USB product ID %q: %w", parts[1], err)
|
||||
}
|
||||
preferredPortIDs = append(preferredPortIDs, [2]uint16{uint16(vid), uint16(pid)})
|
||||
}
|
||||
|
||||
var primaryPorts []string // ports picked from preferred USB VID/PID
|
||||
var secondaryPorts []string // other ports (as a fallback)
|
||||
for _, p := range portsList {
|
||||
if !p.IsUSB {
|
||||
continue
|
||||
}
|
||||
if p.VID != "" && p.PID != "" {
|
||||
foundPort := false
|
||||
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
|
||||
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
|
||||
if vidErr == nil && pidErr == nil {
|
||||
for _, id := range preferredPortIDs {
|
||||
if uint16(vid) == id[0] && uint16(pid) == id[1] {
|
||||
primaryPorts = append(primaryPorts, p.Name)
|
||||
foundPort = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if foundPort {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
secondaryPorts = append(secondaryPorts, p.Name)
|
||||
}
|
||||
if len(primaryPorts) == 1 {
|
||||
// There is exactly one match in the set of preferred ports. Use
|
||||
// this port, even if there may be others available. This allows
|
||||
// flashing a specific board even if there are multiple available.
|
||||
return primaryPorts[0], nil
|
||||
} else if len(primaryPorts) > 1 {
|
||||
// There are multiple preferred ports, probably because more than
|
||||
// one device of the same type are connected (e.g. two Arduino
|
||||
// Unos).
|
||||
ports = primaryPorts
|
||||
} else {
|
||||
// No preferred ports found. Fall back to other serial ports
|
||||
// available in the system.
|
||||
ports = secondaryPorts
|
||||
ports = append(ports, p.Name)
|
||||
}
|
||||
|
||||
if len(ports) == 0 {
|
||||
if ports == nil || len(ports) == 0 {
|
||||
// fallback
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
@@ -875,35 +727,6 @@ func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err e
|
||||
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
|
||||
}
|
||||
|
||||
// getBMPPorts returns BlackMagicProbe's serial ports if any
|
||||
func getBMPPorts() (gdbPort, uartPort string, err error) {
|
||||
var portsList []*enumerator.PortDetails
|
||||
portsList, err = enumerator.GetDetailedPortsList()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
var ports []string
|
||||
for _, p := range portsList {
|
||||
if !p.IsUSB {
|
||||
continue
|
||||
}
|
||||
if p.VID != "" && p.PID != "" {
|
||||
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
|
||||
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
|
||||
if vidErr == nil && pidErr == nil && vid == 0x1d50 && pid == 0x6018 {
|
||||
ports = append(ports, p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(ports) == 2 {
|
||||
return ports[0], ports[1], nil
|
||||
} else if len(ports) == 0 {
|
||||
return "", "", errors.New("no BMP detected")
|
||||
} else {
|
||||
return "", "", fmt.Errorf("expected 2 BMP serial ports, found %d - did you perhaps connect more than one BMP?", len(ports))
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
|
||||
@@ -1072,7 +895,6 @@ func main() {
|
||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
|
||||
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
||||
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks)")
|
||||
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
|
||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||
verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR")
|
||||
@@ -1082,30 +904,25 @@ func main() {
|
||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
||||
printCommands := flag.Bool("x", false, "Print commands")
|
||||
nodebug := flag.Bool("no-debug", false, "strip debug information")
|
||||
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
|
||||
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 (can specify multiple candidates separated by commas)")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
|
||||
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
|
||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||
|
||||
var flagJSON, flagDeps, flagTest *bool
|
||||
var flagJSON, flagDeps *bool
|
||||
if command == "help" || command == "list" {
|
||||
flagJSON = flag.Bool("json", false, "print data in JSON format")
|
||||
flagDeps = flag.Bool("deps", false, "supply -deps flag to go list")
|
||||
flagTest = flag.Bool("test", false, "supply -test flag to go list")
|
||||
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, testVerboseFlag *bool
|
||||
var testCompileOnlyFlag *bool
|
||||
if command == "help" || command == "test" {
|
||||
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
|
||||
testVerboseFlag = flag.Bool("v", false, "verbose: print additional output")
|
||||
}
|
||||
|
||||
// Early command processing, before commands are interpreted by the Go flag
|
||||
@@ -1126,7 +943,6 @@ func main() {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var printAllocs *regexp.Regexp
|
||||
if *printAllocsString != "" {
|
||||
printAllocs, err = regexp.Compile(*printAllocsString)
|
||||
@@ -1135,35 +951,24 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var ocdCommands []string
|
||||
if *ocdCommandsString != "" {
|
||||
ocdCommands = strings.Split(*ocdCommandsString, ",")
|
||||
}
|
||||
|
||||
options := &compileopts.Options{
|
||||
Target: *target,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
Serial: *serial,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
Tags: *tags,
|
||||
GlobalValues: globalVarValues,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
OpenOCDCommands: ocdCommands,
|
||||
LLVMFeatures: *llvmFeatures,
|
||||
}
|
||||
if *printCommands {
|
||||
options.PrintCommands = printCommand
|
||||
Target: *target,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
GlobalValues: globalVarValues,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
}
|
||||
|
||||
os.Setenv("CC", "clang -target="+*target)
|
||||
@@ -1175,20 +980,6 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *cpuprofile != "" {
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "could not create CPU profile: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "could not start CPU profile: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "build":
|
||||
if outpath == "" {
|
||||
@@ -1270,26 +1061,16 @@ func main() {
|
||||
err := Run(pkgName, options)
|
||||
handleCompilerError(err)
|
||||
case "test":
|
||||
var pkgNames []string
|
||||
for i := 0; i < flag.NArg(); i++ {
|
||||
pkgNames = append(pkgNames, filepath.ToSlash(flag.Arg(i)))
|
||||
}
|
||||
if len(pkgNames) == 0 {
|
||||
pkgNames = []string{"."}
|
||||
}
|
||||
allTestsPassed := true
|
||||
for _, pkgName := range pkgNames {
|
||||
// TODO: parallelize building the test binaries
|
||||
passed, err := Test(pkgName, options, *testCompileOnlyFlag, *testVerboseFlag, outpath)
|
||||
handleCompilerError(err)
|
||||
if !passed {
|
||||
allTestsPassed = false
|
||||
}
|
||||
}
|
||||
if !allTestsPassed {
|
||||
fmt.Println("FAIL")
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = filepath.ToSlash(flag.Arg(0))
|
||||
} else if flag.NArg() > 1 {
|
||||
fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
|
||||
handleCompilerError(err)
|
||||
case "targets":
|
||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
@@ -1364,9 +1145,6 @@ func main() {
|
||||
if *flagDeps {
|
||||
extraArgs = append(extraArgs, "-deps")
|
||||
}
|
||||
if *flagTest {
|
||||
extraArgs = append(extraArgs, "-test")
|
||||
}
|
||||
cmd, err := loader.List(config, extraArgs, flag.Args())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
||||
|
||||
+65
-88
@@ -35,9 +35,9 @@ func TestCompiler(t *testing.T) {
|
||||
"calls.go",
|
||||
"cgo/",
|
||||
"channel.go",
|
||||
"coroutines.go",
|
||||
"float.go",
|
||||
"gc.go",
|
||||
"goroutines.go",
|
||||
"init.go",
|
||||
"init_multi.go",
|
||||
"interface.go",
|
||||
@@ -51,18 +51,9 @@ func TestCompiler(t *testing.T) {
|
||||
"stdlib.go",
|
||||
"string.go",
|
||||
"structs.go",
|
||||
"testing.go",
|
||||
"zeroalloc.go",
|
||||
}
|
||||
|
||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read version from GOROOT:", err)
|
||||
}
|
||||
if minor >= 17 {
|
||||
tests = append(tests, "go1.17.go")
|
||||
}
|
||||
|
||||
if *testTarget != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
// which is especially useful to quickly check whether some changes
|
||||
@@ -103,9 +94,21 @@ func TestCompiler(t *testing.T) {
|
||||
t.Run("ARM64Linux", func(t *testing.T) {
|
||||
runPlatTests("aarch64--linux-gnu", tests, t)
|
||||
})
|
||||
t.Run("WebAssembly", func(t *testing.T) {
|
||||
runPlatTests("wasm", tests, t)
|
||||
})
|
||||
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Error("could not get Go version:", err)
|
||||
return
|
||||
}
|
||||
minorVersion := strings.Split(goVersion, ".")[1]
|
||||
if minorVersion != "13" {
|
||||
// WebAssembly tests fail on Go 1.13, so skip them there. Versions
|
||||
// below that are also not supported but still seem to pass, so
|
||||
// include them in the tests for now.
|
||||
t.Run("WebAssembly", func(t *testing.T) {
|
||||
runPlatTests("wasm", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("WASI", func(t *testing.T) {
|
||||
runPlatTests("wasi", tests, t)
|
||||
})
|
||||
@@ -122,7 +125,7 @@ func TestCompiler(t *testing.T) {
|
||||
// Test with few optimizations enabled (no inlining, etc).
|
||||
t.Run("opt=1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("stdlib.go", "", t, compileopts.Options{
|
||||
runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
|
||||
Opt: "1",
|
||||
}, nil, nil)
|
||||
})
|
||||
@@ -131,14 +134,15 @@ func TestCompiler(t *testing.T) {
|
||||
// TODO: fix this for stdlib.go, which currently fails.
|
||||
t.Run("opt=0", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("print.go", "", t, compileopts.Options{
|
||||
runTestWithConfig("print.go", "", t, &compileopts.Options{
|
||||
Opt: "0",
|
||||
}, nil, nil)
|
||||
})
|
||||
|
||||
t.Run("ldflags", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("ldflags.go", "", t, compileopts.Options{
|
||||
runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
|
||||
Opt: "z",
|
||||
GlobalValues: map[string]map[string]string{
|
||||
"main": {
|
||||
"someGlobal": "foobar",
|
||||
@@ -159,20 +163,14 @@ func runPlatTests(target string, tests []string, t *testing.T) {
|
||||
runTest(name, target, t, nil, nil)
|
||||
})
|
||||
}
|
||||
t.Run("env.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||
})
|
||||
if target == "" || target == "wasi" {
|
||||
if target == "wasi" || target == "" {
|
||||
t.Run("filesystem.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("filesystem.go", target, t, nil, nil)
|
||||
})
|
||||
}
|
||||
if target == "" || target == "wasi" || target == "wasm" {
|
||||
t.Run("rand.go", func(t *testing.T) {
|
||||
t.Run("env.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("rand.go", target, t, nil, nil)
|
||||
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -190,20 +188,20 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
||||
}
|
||||
|
||||
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
|
||||
options := compileopts.Options{
|
||||
Target: target,
|
||||
options := &compileopts.Options{
|
||||
Target: target,
|
||||
Opt: "z",
|
||||
PrintIR: false,
|
||||
DumpSSA: false,
|
||||
VerifyIR: true,
|
||||
Debug: true,
|
||||
PrintSizes: "",
|
||||
WasmAbi: "",
|
||||
}
|
||||
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
|
||||
}
|
||||
|
||||
func runTestWithConfig(name, target string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) {
|
||||
// Set default config.
|
||||
options.Debug = true
|
||||
options.VerifyIR = true
|
||||
if options.Opt == "" {
|
||||
options.Opt = "z"
|
||||
}
|
||||
|
||||
func runTestWithConfig(name, target string, t *testing.T, options *compileopts.Options, cmdArgs, environmentVars []string) {
|
||||
// Get the expected output for this test.
|
||||
// Note: not using filepath.Join as it strips the path separator at the end
|
||||
// of the path.
|
||||
@@ -219,78 +217,57 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
|
||||
}
|
||||
|
||||
// Create a temporary directory for test output files.
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
// Determine whether we're on a system that supports environment variables
|
||||
// and command line parameters (operating systems, WASI) or not (baremetal,
|
||||
// WebAssembly in the browser). If we're on a system without an environment,
|
||||
// we need to pass command line arguments and environment variables through
|
||||
// global variables (built into the binary directly) instead of the
|
||||
// conventional way.
|
||||
spec, err := compileopts.LoadTarget(target)
|
||||
tmpdir, err := ioutil.TempDir("", "tinygo-test")
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
t.Fatal("could not create temporary directory:", err)
|
||||
}
|
||||
needsEnvInVars := spec.GOOS == "js"
|
||||
for _, tag := range spec.BuildTags {
|
||||
if tag == "baremetal" {
|
||||
needsEnvInVars = true
|
||||
defer func() {
|
||||
rerr := os.RemoveAll(tmpdir)
|
||||
if rerr != nil {
|
||||
t.Errorf("failed to remove temporary directory %q: %s", tmpdir, rerr.Error())
|
||||
}
|
||||
}
|
||||
if needsEnvInVars {
|
||||
runtimeGlobals := make(map[string]string)
|
||||
if len(cmdArgs) != 0 {
|
||||
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
|
||||
}
|
||||
if len(environmentVars) != 0 {
|
||||
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
|
||||
}
|
||||
if len(runtimeGlobals) != 0 {
|
||||
// This sets the global variables like they would be set with
|
||||
// `-ldflags="-X=runtime.osArgs=first\x00second`.
|
||||
// The runtime package has two variables (osArgs and osEnv) that are
|
||||
// both strings, from which the parameters and environment variables
|
||||
// are read.
|
||||
options.GlobalValues = map[string]map[string]string{
|
||||
"runtime": runtimeGlobals,
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Build the test binary.
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = runBuild("./"+path, binary, &options)
|
||||
err = runBuild("./"+path, binary, options)
|
||||
if err != nil {
|
||||
printCompilerError(t.Log, err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
// Create the test command, taking care of emulators etc.
|
||||
// Run the test.
|
||||
runComplete := make(chan struct{})
|
||||
var cmd *exec.Cmd
|
||||
if len(spec.Emulator) == 0 {
|
||||
ranTooLong := false
|
||||
if target == "" {
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
// Allow reading from the current directory.
|
||||
cmd.Args = append(cmd.Args, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
if !needsEnvInVars {
|
||||
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
|
||||
spec, err := compileopts.LoadTarget(target)
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
}
|
||||
if len(spec.Emulator) == 0 {
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
// Allow reading from the current directory.
|
||||
cmd.Args = append(cmd.Args, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
runComplete := make(chan struct{})
|
||||
ranTooLong := false
|
||||
stdout := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package rand implements a cryptographically secure
|
||||
// random number generator.
|
||||
package rand
|
||||
|
||||
import "io"
|
||||
|
||||
// Reader is a global, shared instance of a cryptographically
|
||||
// secure random number generator.
|
||||
var Reader io.Reader
|
||||
|
||||
// Read is a helper function that calls Reader.Read using io.ReadFull.
|
||||
// On return, n == len(b) if and only if err == nil.
|
||||
func Read(b []byte) (n int, err error) {
|
||||
return io.ReadFull(Reader, b)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// +build darwin freebsd tinygo.wasm
|
||||
|
||||
// This implementation of crypto/rand uses the arc4random_buf function
|
||||
// (available on both MacOS and WASI) to generate random numbers.
|
||||
//
|
||||
// Note: arc4random_buf (unlike what the name suggets) does not use the insecure
|
||||
// RC4 cipher. Instead, it uses a high-quality cipher, varying by the libc
|
||||
// implementation.
|
||||
|
||||
package rand
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func init() {
|
||||
Reader = &reader{}
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
}
|
||||
|
||||
func (r *reader) Read(b []byte) (n int, err error) {
|
||||
if len(b) != 0 {
|
||||
libc_arc4random_buf(unsafe.Pointer(&b[0]), uint(len(b)))
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// void arc4random_buf(void *buf, size_t buflen);
|
||||
//export arc4random_buf
|
||||
func libc_arc4random_buf(buf unsafe.Pointer, buflen uint)
|
||||
@@ -1,38 +0,0 @@
|
||||
// +build linux,!baremetal,!wasi
|
||||
|
||||
// This implementation of crypto/rand uses the /dev/urandom pseudo-file to
|
||||
// generate random numbers.
|
||||
// TODO: convert to the getentropy or getrandom libc function on Linux once it
|
||||
// is more widely supported.
|
||||
|
||||
package rand
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Reader = &reader{}
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
fd int
|
||||
}
|
||||
|
||||
func (r *reader) Read(b []byte) (n int, err error) {
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Open /dev/urandom first if needed.
|
||||
if r.fd == 0 {
|
||||
fd, err := syscall.Open("/dev/urandom", syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r.fd = fd
|
||||
}
|
||||
|
||||
// Read from the file.
|
||||
return syscall.Read(r.fd, b)
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package rand
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// smallPrimes is a list of small, prime numbers that allows us to rapidly
|
||||
// exclude some fraction of composite candidates when searching for a random
|
||||
// prime. This list is truncated at the point where smallPrimesProduct exceeds
|
||||
// a uint64. It does not include two because we ensure that the candidates are
|
||||
// odd by construction.
|
||||
var smallPrimes = []uint8{
|
||||
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
|
||||
}
|
||||
|
||||
// smallPrimesProduct is the product of the values in smallPrimes and allows us
|
||||
// to reduce a candidate prime by this number and then determine whether it's
|
||||
// coprime to all the elements of smallPrimes without further big.Int
|
||||
// operations.
|
||||
var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365)
|
||||
|
||||
// Prime returns a number, p, of the given size, such that p is prime
|
||||
// with high probability.
|
||||
// Prime will return error for any error returned by rand.Read or if bits < 2.
|
||||
func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
|
||||
if bits < 2 {
|
||||
err = errors.New("crypto/rand: prime size must be at least 2-bit")
|
||||
return
|
||||
}
|
||||
|
||||
b := uint(bits % 8)
|
||||
if b == 0 {
|
||||
b = 8
|
||||
}
|
||||
|
||||
bytes := make([]byte, (bits+7)/8)
|
||||
p = new(big.Int)
|
||||
|
||||
bigMod := new(big.Int)
|
||||
|
||||
for {
|
||||
_, err = io.ReadFull(rand, bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clear bits in the first byte to make sure the candidate has a size <= bits.
|
||||
bytes[0] &= uint8(int(1<<b) - 1)
|
||||
// Don't let the value be too small, i.e, set the most significant two bits.
|
||||
// Setting the top two bits, rather than just the top bit,
|
||||
// means that when two of these values are multiplied together,
|
||||
// the result isn't ever one bit short.
|
||||
if b >= 2 {
|
||||
bytes[0] |= 3 << (b - 2)
|
||||
} else {
|
||||
// Here b==1, because b cannot be zero.
|
||||
bytes[0] |= 1
|
||||
if len(bytes) > 1 {
|
||||
bytes[1] |= 0x80
|
||||
}
|
||||
}
|
||||
// Make the value odd since an even number this large certainly isn't prime.
|
||||
bytes[len(bytes)-1] |= 1
|
||||
|
||||
p.SetBytes(bytes)
|
||||
|
||||
// Calculate the value mod the product of smallPrimes. If it's
|
||||
// a multiple of any of these primes we add two until it isn't.
|
||||
// The probability of overflowing is minimal and can be ignored
|
||||
// because we still perform Miller-Rabin tests on the result.
|
||||
bigMod.Mod(p, smallPrimesProduct)
|
||||
mod := bigMod.Uint64()
|
||||
|
||||
NextDelta:
|
||||
for delta := uint64(0); delta < 1<<20; delta += 2 {
|
||||
m := mod + delta
|
||||
for _, prime := range smallPrimes {
|
||||
if m%uint64(prime) == 0 && (bits > 6 || m != uint64(prime)) {
|
||||
continue NextDelta
|
||||
}
|
||||
}
|
||||
|
||||
if delta > 0 {
|
||||
bigMod.SetUint64(delta)
|
||||
p.Add(p, bigMod)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// There is a tiny possibility that, by adding delta, we caused
|
||||
// the number to be one bit too long. Thus we check BitLen
|
||||
// here.
|
||||
if p.ProbablyPrime(20) && p.BitLen() == bits {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Int returns a uniform random value in [0, max). It panics if max <= 0.
|
||||
func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) {
|
||||
if max.Sign() <= 0 {
|
||||
panic("crypto/rand: argument to Int is <= 0")
|
||||
}
|
||||
n = new(big.Int)
|
||||
n.Sub(max, n.SetUint64(1))
|
||||
// bitLen is the maximum bit length needed to encode a value < max.
|
||||
bitLen := n.BitLen()
|
||||
if bitLen == 0 {
|
||||
// the only valid result is 0
|
||||
return
|
||||
}
|
||||
// k is the maximum byte length needed to encode a value < max.
|
||||
k := (bitLen + 7) / 8
|
||||
// b is the number of bits in the most significant byte of max-1.
|
||||
b := uint(bitLen % 8)
|
||||
if b == 0 {
|
||||
b = 8
|
||||
}
|
||||
|
||||
bytes := make([]byte, k)
|
||||
|
||||
for {
|
||||
_, err = io.ReadFull(rand, bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clear bits in the first byte to increase the probability
|
||||
// that the candidate is < max.
|
||||
bytes[0] &= uint8(int(1<<b) - 1)
|
||||
|
||||
n.SetBytes(bytes)
|
||||
if n.Cmp(max) < 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-15
@@ -26,21 +26,12 @@ type SCB_Type struct {
|
||||
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
|
||||
AFSR volatile.Register32 // 0xD3C: Auxiliary Fault Status Register
|
||||
PFR [2]volatile.Register32 // 0xD40: Processor Feature Register
|
||||
DFR volatile.Register32 // 0xD48: Debug Feature Register
|
||||
ADR volatile.Register32 // 0xD4C: Auxiliary Feature Register
|
||||
MMFR [4]volatile.Register32 // 0xD50: Memory Model Feature Register
|
||||
ISAR [5]volatile.Register32 // 0xD60: Instruction Set Attributes Register
|
||||
_ [5]uint32 // reserved
|
||||
CPACR volatile.Register32 // 0xD88: Coprocessor Access Control Register
|
||||
|
||||
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)))
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// This is a very minimal bootloader for the ESP32-C3. It only initializes the
|
||||
// flash and then continues with the generic RISC-V initialization code, which
|
||||
// in turn will call runtime.main.
|
||||
// It is written in assembly (and not in a higher level language) to make sure
|
||||
// it is entirely loaded into IRAM and doesn't accidentally call functions
|
||||
// stored in IROM.
|
||||
//
|
||||
// For reference, here is a nice introduction into RISC-V assembly:
|
||||
// https://www.imperialviolet.org/2016/12/31/riscv.html
|
||||
|
||||
.section .init
|
||||
.global call_start_cpu0
|
||||
.type call_start_cpu0,@function
|
||||
call_start_cpu0:
|
||||
// At this point:
|
||||
// - The ROM bootloader is finished and has jumped to here.
|
||||
// - We're running from IRAM: both IRAM and DRAM segments have been loaded
|
||||
// by the ROM bootloader.
|
||||
// - We have a usable stack (but not the one we would like to use).
|
||||
// - No flash mappings (MMU) are set up yet.
|
||||
|
||||
// Reset MMU, see bootloader_reset_mmu in the ESP-IDF.
|
||||
call Cache_Suspend_ICache
|
||||
mv s0, a0 // autoload value
|
||||
call Cache_Invalidate_ICache_All
|
||||
call Cache_MMU_Init
|
||||
|
||||
// Set up DROM from flash.
|
||||
// Somehow, this also sets up IROM from flash. Not sure why, but it avoids
|
||||
// the need for another such call.
|
||||
// C equivalent:
|
||||
// Cache_Dbus_MMU_Set(MMU_ACCESS_FLASH, 0x3C00_0000, 0, 64, 128, 0)
|
||||
li a0, 0 // ext_ram: MMU_ACCESS_FLASH
|
||||
li a1, 0x3C000000 // vaddr: address in the data bus
|
||||
li a2, 0 // paddr: physical address in the flash chip
|
||||
li a3, 64 // psize: always 64 (kilobytes)
|
||||
li a4, 128 // num: pages to be set (8192K / 64K = 128)
|
||||
li a5, 0 // fixed
|
||||
call Cache_Dbus_MMU_Set
|
||||
|
||||
// Enable the flash cache.
|
||||
mv a0, s0 // restore autoload value from Cache_Suspend_ICache call
|
||||
call Cache_Resume_ICache
|
||||
|
||||
// Jump to generic RISC-V initialization, which initializes the stack
|
||||
// pointer and globals register. It should not return.
|
||||
// (It appears that the linker relaxes this jump and instead inserts the
|
||||
// _start function right after here).
|
||||
j _start
|
||||
@@ -2,7 +2,6 @@
|
||||
// Type definitions, fields, and constants associated with the MPU peripheral
|
||||
// of the NXP MIMXRT1062.
|
||||
|
||||
//go:build nxp && mimxrt1062
|
||||
// +build nxp,mimxrt1062
|
||||
|
||||
package nxp
|
||||
@@ -217,12 +216,6 @@ func enableIcache(enable bool) {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
dcacheCcsidr volatile.Register32
|
||||
dcacheSets volatile.Register32
|
||||
dcacheWays volatile.Register32
|
||||
)
|
||||
|
||||
func enableDcache(enable bool) {
|
||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
|
||||
if enable {
|
||||
@@ -251,6 +244,11 @@ func enableDcache(enable bool) {
|
||||
isb 0xF
|
||||
`, nil)
|
||||
} else {
|
||||
var (
|
||||
ccsidr volatile.Register32
|
||||
sets volatile.Register32
|
||||
ways volatile.Register32
|
||||
)
|
||||
SystemControl.CSSELR.Set(0)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
@@ -259,17 +257,17 @@ func enableDcache(enable bool) {
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
|
||||
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
|
||||
for dcacheSets.Get() != 0 {
|
||||
dcacheWays.Set((dcacheCcsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
|
||||
for dcacheWays.Get() != 0 {
|
||||
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(
|
||||
((dcacheSets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
|
||||
((dcacheWays.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk))
|
||||
dcacheWays.Set(dcacheWays.Get() - 1)
|
||||
((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)
|
||||
}
|
||||
dcacheSets.Set(dcacheSets.Get() - 1)
|
||||
sets.Set(sets.Get() - 1)
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// +build feather_m4_can
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// power on the CAN Transceiver
|
||||
// https://learn.adafruit.com/adafruit-feather-m4-can-express/pinouts#can-bus-3078990-8
|
||||
boost_en := machine.BOOST_EN
|
||||
boost_en.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
boost_en.High()
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
can1 := machine.CAN1
|
||||
can1.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN1_RX,
|
||||
Tx: machine.CAN1_TX,
|
||||
Standby: machine.CAN1_STANDBY,
|
||||
})
|
||||
|
||||
can0 := machine.CAN0
|
||||
can0.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN0_RX,
|
||||
Tx: machine.CAN0_TX,
|
||||
Standby: machine.NoPin,
|
||||
})
|
||||
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
|
||||
for {
|
||||
can1.Tx(0x123, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, false, false)
|
||||
can1.Tx(0x789, []byte{0x02, 0x24, 0x46, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, true, false)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
sz0 := can0.RxFifoSize()
|
||||
if sz0 > 0 {
|
||||
fmt.Printf("CAN0 %d\r\n", sz0)
|
||||
for i := 0; i < sz0; i++ {
|
||||
can0.RxRaw(&rxMsg)
|
||||
fmt.Printf("-> %08X %X %#v\r\n", rxMsg.ID, rxMsg.DLC, rxMsg.Data())
|
||||
}
|
||||
}
|
||||
|
||||
sz1 := can1.RxFifoSize()
|
||||
if sz1 > 0 {
|
||||
fmt.Printf("CAN1 %d\r\n", sz1)
|
||||
for i := 0; i < sz1; i++ {
|
||||
can1.RxRaw(&rxMsg)
|
||||
fmt.Printf("-> %08X %X %#v\r\n", rxMsg.ID, rxMsg.DLC, rxMsg.Data())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// +build feather_m4_can
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// power on the CAN Transceiver
|
||||
// https://learn.adafruit.com/adafruit-feather-m4-can-express/pinouts#can-bus-3078990-8
|
||||
boost_en := machine.BOOST_EN
|
||||
boost_en.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
boost_en.High()
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type canMsg struct {
|
||||
ch byte
|
||||
id uint32
|
||||
dlc byte
|
||||
data []byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
ch := make(chan canMsg, 10)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case m := <-ch:
|
||||
fmt.Printf("%d %03X %X ", m.ch, m.id, m.dlc)
|
||||
for _, d := range m.data {
|
||||
fmt.Printf("%02X ", d)
|
||||
}
|
||||
fmt.Printf("\r\n")
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
can1 := machine.CAN1
|
||||
can1.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN1_RX,
|
||||
Tx: machine.CAN1_TX,
|
||||
Standby: machine.CAN1_STANDBY,
|
||||
})
|
||||
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||
can1.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
can1.RxRaw(&rxMsg)
|
||||
m := canMsg{ch: 1, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||
select {
|
||||
case ch <- m:
|
||||
}
|
||||
})
|
||||
|
||||
can0 := machine.CAN0
|
||||
can0.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN0_RX,
|
||||
Tx: machine.CAN0_TX,
|
||||
Standby: machine.NoPin,
|
||||
})
|
||||
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||
can0.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
can0.RxRaw(&rxMsg)
|
||||
m := canMsg{ch: 2, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||
select {
|
||||
case ch <- m:
|
||||
}
|
||||
})
|
||||
|
||||
for {
|
||||
can0.Tx(0x123, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, false, false)
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
can1.Tx(0x456, []byte{0xAA, 0xBB, 0xCC}, false, false)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// change these to test a different UART or pins if available
|
||||
var (
|
||||
uart = machine.Serial
|
||||
uart = machine.UART0
|
||||
tx = machine.UART_TX_PIN
|
||||
rx = machine.UART_RX_PIN
|
||||
)
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
ms := runtime.MemStats{}
|
||||
|
||||
for {
|
||||
escapesToHeap()
|
||||
runtime.ReadMemStats(&ms)
|
||||
println("Heap before GC. Used: ", ms.HeapInuse, " Free: ", ms.HeapIdle, " Meta: ", ms.GCSys)
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&ms)
|
||||
println("Heap after GC. Used: ", ms.HeapInuse, " Free: ", ms.HeapIdle, " Meta: ", ms.GCSys)
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func escapesToHeap() {
|
||||
n := rand.Intn(100)
|
||||
println("Doing ", n, " iterations")
|
||||
for i := 0; i < n; i++ {
|
||||
s := make([]byte, i)
|
||||
_ = append(s, 42)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// +build stm32
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
buttonMode = machine.PinInputPulldown
|
||||
buttonPinChange = machine.PinRising | machine.PinFalling
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build bluepill
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = &machine.TIM2
|
||||
pinA = machine.PA0
|
||||
pinB = machine.PA1
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build stm32f7
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = &machine.TIM1
|
||||
pinA = machine.PA8
|
||||
pinB = machine.PA9
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build stm32l0
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = &machine.TIM2
|
||||
pinA = machine.PA0
|
||||
pinB = machine.PB3
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build stm32l4
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = &machine.TIM2
|
||||
pinA = machine.PA0
|
||||
pinB = machine.PB3
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build stm32l5
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = &machine.TIM1
|
||||
pinA = machine.PA8
|
||||
pinB = machine.PA9
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build pico
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM4 // Pin 25 (LED on pico) corresponds to PWM4.
|
||||
pinA = machine.LED
|
||||
pinB = machine.GPIO24
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
// +build stm32f4disco
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
// These pins correspond to LEDs on the discovery
|
||||
// board
|
||||
pwm = &machine.TIM4
|
||||
pinA = machine.PD12
|
||||
pinB = machine.PD13
|
||||
)
|
||||
@@ -13,7 +13,7 @@ type Task struct {
|
||||
Ptr unsafe.Pointer
|
||||
|
||||
// Data is a field which can be used for storing state information.
|
||||
Data uint64
|
||||
Data uint
|
||||
|
||||
// state is the underlying running state of the task.
|
||||
state state
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
.cfi_startproc
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, EBX contain the pc of the to-be-started function and
|
||||
// ESI contain the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Indicate to the unwinder that there is nothing to unwind, this is the
|
||||
// root frame. It avoids bogus extra frames in GDB.
|
||||
.cfi_undefined eip
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
pushl %esi
|
||||
|
||||
// Branch to the "goroutine start" function.
|
||||
calll *%ebx
|
||||
|
||||
// Rebalance the stack (to undo the above push).
|
||||
addl $4, %esp
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
jmp tinygo_pause
|
||||
.cfi_endproc
|
||||
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
movl 4(%esp), %eax // newStack uintptr
|
||||
movl 8(%esp), %ecx // oldStack *uintptr
|
||||
// More information on the calling convention:
|
||||
// https://wiki.osdev.org/System_V_ABI#i386
|
||||
|
||||
// Save all callee-saved registers:
|
||||
pushl %ebp
|
||||
pushl %edi
|
||||
pushl %esi
|
||||
pushl %ebx
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
movl %esp, (%ecx)
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
movl %eax, %esp
|
||||
|
||||
// Load saved register from the new stack.
|
||||
popl %ebx
|
||||
popl %esi
|
||||
popl %edi
|
||||
popl %ebp
|
||||
|
||||
// Return into the new task, as if tinygo_swapTask was a regular call.
|
||||
ret
|
||||
@@ -1,62 +0,0 @@
|
||||
// +build scheduler.tasks,386
|
||||
|
||||
package task
|
||||
|
||||
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_386.S that relies on the exact
|
||||
// layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
ebx uintptr
|
||||
esi uintptr
|
||||
edi uintptr
|
||||
ebp uintptr
|
||||
|
||||
pc uintptr
|
||||
|
||||
// Pad this struct so that tasks start on a 16-byte aligned stack.
|
||||
_ [3]uintptr
|
||||
}
|
||||
|
||||
// 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_386.S). This assembly code calls a function
|
||||
// (passed in EBX) with a single argument (passed in ESI). After the
|
||||
// function returns, it calls Pause().
|
||||
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||
|
||||
// Pass the function to call in EBX.
|
||||
// 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.ebx = fn
|
||||
|
||||
// Pass the pointer to the arguments struct in ESI.
|
||||
r.esi = 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
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user