Compare commits

..

1 Commits

Author SHA1 Message Date
sago35 8a3f96b221 builder: fixed a problem with multiple process build cases 2021-04-24 09:59:14 +09:00
407 changed files with 5133 additions and 12054 deletions
+14 -14
View File
@@ -157,7 +157,7 @@ commands:
- llvm-source-linux - llvm-source-linux
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-linux-v4-assert - llvm-build-11-linux-v3-assert
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
@@ -176,7 +176,7 @@ commands:
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi fi
- save_cache: - save_cache:
key: llvm-build-11-linux-v4-assert key: llvm-build-11-linux-v3-assert
paths: paths:
llvm-build llvm-build
- run: make ASSERT=1 - run: make ASSERT=1
@@ -225,7 +225,7 @@ commands:
- llvm-source-linux - llvm-source-linux
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-linux-v4-noassert - llvm-build-11-linux-v3-noassert
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
@@ -244,7 +244,7 @@ commands:
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi fi
- save_cache: - save_cache:
key: llvm-build-11-linux-v4-noassert key: llvm-build-11-linux-v3-noassert
paths: paths:
llvm-build llvm-build
- build-wasi-libc - build-wasi-libc
@@ -294,16 +294,16 @@ commands:
variant: "macos" variant: "macos"
- restore_cache: - restore_cache:
keys: keys:
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-macos-v3-{{ checksum "go.mod" }} - go-cache-macos-v2-{{ checksum "go.mod" }}
- restore_cache: - restore_cache:
keys: keys:
- llvm-source-11-macos-v3 - llvm-source-11-macos-v2
- run: - run:
name: "Fetch LLVM source" name: "Fetch LLVM source"
command: make llvm-source command: make llvm-source
- save_cache: - save_cache:
key: llvm-source-11-macos-v3 key: llvm-source-11-macos-v2
paths: paths:
- llvm-project/clang/lib/Headers - llvm-project/clang/lib/Headers
- llvm-project/clang/include - llvm-project/clang/include
@@ -311,7 +311,7 @@ commands:
- llvm-project/llvm/include - llvm-project/llvm/include
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-macos-v5 - llvm-build-11-macos-v3
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
@@ -327,17 +327,17 @@ commands:
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi fi
- save_cache: - save_cache:
key: llvm-build-11-macos-v5 key: llvm-build-11-macos-v3
paths: paths:
llvm-build llvm-build
- restore_cache: - restore_cache:
keys: keys:
- wasi-libc-sysroot-macos-v4 - wasi-libc-sysroot-macos-v3
- run: - run:
name: "Build wasi-libc" name: "Build wasi-libc"
command: make wasi-libc command: make wasi-libc
- save_cache: - save_cache:
key: wasi-libc-sysroot-macos-v4 key: wasi-libc-sysroot-macos-v3
paths: paths:
- lib/wasi-libc/sysroot - lib/wasi-libc/sysroot
- run: - run:
@@ -359,7 +359,7 @@ commands:
tinygo version tinygo version
- run: make smoketest AVR=0 - run: make smoketest AVR=0
- save_cache: - 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: paths:
- ~/.cache/go-build - ~/.cache/go-build
- /go/pkg/mod - /go/pkg/mod
@@ -401,7 +401,7 @@ jobs:
- build-linux - build-linux
build-macos: build-macos:
macos: macos:
xcode: "11.1.0" # macOS 10.14 xcode: "10.1.0"
steps: steps:
- build-macos - build-macos
@@ -1,45 +0,0 @@
name: CI for tinygo-dev docker container
on:
push:
branches: [ dev ]
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 }}
-2
View File
@@ -16,8 +16,6 @@ src/device/stm32/*.go
src/device/stm32/*.s src/device/stm32/*.s
src/device/kendryte/*.go src/device/kendryte/*.go
src/device/kendryte/*.s src/device/kendryte/*.s
src/device/rp/*.go
src/device/rp/*.s
vendor vendor
llvm-build llvm-build
llvm-project llvm-project
-188
View File
@@ -1,191 +1,3 @@
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 0.17.0
--- ---
+2 -2
View File
@@ -1,5 +1,5 @@
# TinyGo base stage installs the most recent Go 1.16.x, LLVM 11 and the TinyGo compiler itself. # TinyGo base stage installs the most recent Go 1.15.x, LLVM 11 and the TinyGo compiler itself.
FROM golang:1.16 AS tinygo-base FROM golang:1.15 AS tinygo-base
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \ RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \ echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
+21 -49
View File
@@ -36,7 +36,7 @@ else
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF' LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
endif 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 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 endif
# Libraries that should be linked in for the statically linked Clang. # 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++ CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD. # Libraries that should be linked in for the statically linked LLD.
@@ -107,7 +107,7 @@ fmt-check:
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1 @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) ifneq ($(STM32), 0)
gen-device: gen-device-stm32 gen-device: gen-device-stm32
endif endif
@@ -150,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/ ./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 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. # Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/llvm:
@@ -162,7 +159,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: llvm-source $(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. # Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
@@ -185,28 +182,25 @@ tinygo:
test: wasi-libc 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 . 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 \
encoding \
encoding/ascii85 \
encoding/base32 \
encoding/hex \
hash/adler32 \
hash/fnv \
hash/crc64 \
math \
math/cmplx \
text/scanner \
unicode/utf8 \
# Test known-working standard library packages. # 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 .PHONY: tinygo-test
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 .PHONY: smoketest
smoketest: smoketest:
@@ -230,8 +224,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008 $(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt $(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@@ -336,11 +328,9 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/blinky1
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
@$(MD5SUM) test.hex @$(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 @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -350,22 +340,8 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/blinky1 $(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/blinky1
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/blinky1
@$(MD5SUM) test.hex @$(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=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 # test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -396,8 +372,6 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/pwm
@$(MD5SUM) test.hex
endif endif
ifneq ($(AVR), 0) ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial $(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@@ -438,8 +412,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
@$(MD5SUM) test.nro @$(MD5SUM) test.nro
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go $(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
+2 -13
View File
@@ -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. You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 68 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 Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
@@ -52,10 +52,7 @@ The following 68 microcontroller boards are currently supported:
* [Adafruit Feather M4](https://www.adafruit.com/product/3857) * [Adafruit Feather M4](https://www.adafruit.com/product/3857)
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759) * [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062) * [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 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 M0](https://www.adafruit.com/product/3727)
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800) * [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481) * [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
@@ -66,14 +63,10 @@ The following 68 microcontroller boards are currently supported:
* [Adafruit PyPortal](https://www.adafruit.com/product/4116) * [Adafruit PyPortal](https://www.adafruit.com/product/4116)
* [Adafruit QT Py](https://www.adafruit.com/product/4600) * [Adafruit QT Py](https://www.adafruit.com/product/4600)
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500) * [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 Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi) * [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
* [Arduino Nano](https://store.arduino.cc/arduino-nano) * [Arduino Nano](https://store.arduino.cc/arduino-nano)
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble) * [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
* [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 Uno](https://store.arduino.cc/arduino-uno-rev3) * [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero) * [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
* [BBC micro:bit](https://microbit.org/) * [BBC micro:bit](https://microbit.org/)
@@ -91,7 +84,6 @@ The following 68 microcontroller boards are currently supported:
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle) * [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 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 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 Argon](https://docs.particle.io/datasheets/wi-fi/argon-datasheet/)
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/) * [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/) * [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
@@ -100,15 +92,12 @@ The following 68 microcontroller boards are currently supported:
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html) * [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.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) * [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/)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) * [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 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) * [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) * [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" 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" 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 "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 STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html) * [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- task: CacheBeta@0 - task: CacheBeta@0
displayName: Cache LLVM build displayName: Cache LLVM build
inputs: inputs:
key: llvm-build-11-windows-v5 key: llvm-build-11-windows-v4
path: llvm-build path: llvm-build
- task: Bash@3 - task: Bash@3
displayName: Build LLVM displayName: Build LLVM
+74 -121
View File
@@ -12,9 +12,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"go/types" "go/types"
"hash/crc32"
"io/ioutil" "io/ioutil"
"math/bits"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@@ -41,11 +39,6 @@ type BuildResult struct {
// The directory of the main package. This is useful for testing as the test // 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. // binary must be run in the directory of the tested package.
MainDir string 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 // packageAction is the struct that is serialized to JSON and hashed, to work as
@@ -101,7 +94,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
DefaultStackSize: config.Target.DefaultStackSize, DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(), NeedsStackObjects: config.NeedsStackObjects(),
Debug: config.Debug(), Debug: config.Debug(),
LLVMFeatures: config.LLVMFeatures(),
} }
// Load the target machine, which is the LLVM object that contains all // Load the target machine, which is the LLVM object that contains all
@@ -308,7 +300,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err 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) jobs = append(jobs, job)
@@ -470,10 +468,33 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
linkerDependencies = append(linkerDependencies, 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 // 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 // contain things like the interrupt vector table and low level operations
// such as stack switching. // such as stack switching.
root := goenv.Get("TINYGOROOT")
for _, path := range config.ExtraFiles() { for _, path := range config.ExtraFiles() {
abspath := filepath.Join(root, path) abspath := filepath.Join(root, path)
job := &compileJob{ job := &compileJob{
@@ -514,31 +535,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
ldflags = append(ldflags, lprogram.LDFlags...) 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
}
// 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`?")
}
job := dummyCompileJob(path)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
case "":
// no library specified, so nothing to do
default:
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
}
// Create a linker job, which links all object files together and does some // Create a linker job, which links all object files together and does some
// extra stuff that can only be done after linking. // extra stuff that can only be done after linking.
jobs = append(jobs, &compileJob{ jobs = append(jobs, &compileJob{
@@ -551,8 +547,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
} }
ldflags = append(ldflags, dependency.result) ldflags = append(ldflags, dependency.result)
} }
if config.Options.PrintCommands != nil { if config.Options.PrintCommands {
config.Options.PrintCommands(config.Target.Linker, ldflags...) fmt.Printf("%s %s\n", config.Target.Linker, strings.Join(ldflags, " "))
} }
err = link(config.Target.Linker, ldflags...) err = link(config.Target.Linker, ldflags...)
if err != nil { if err != nil {
@@ -570,8 +566,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err return err
} }
} }
// Apply ELF patches
if config.AutomaticStackSize() { if config.AutomaticStackSize() {
// Modify the .tinygo_stacksizes section that contains a stack size // Modify the .tinygo_stacksizes section that contains a stack size
// for each goroutine. // for each goroutine.
@@ -580,13 +574,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return fmt.Errorf("could not modify stack sizes: %w", err) 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" { if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
sizes, err := loadProgramSize(executable) sizes, err := loadProgramSize(executable)
@@ -652,25 +639,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err 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: default:
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat) return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
} }
return action(BuildResult{ return action(BuildResult{
Binary: tmppath, Binary: tmppath,
MainDir: lprogram.MainPkg().Dir, MainDir: lprogram.MainPkg().Dir,
ImportPath: lprogram.MainPkg().ImportPath,
}) })
} }
@@ -933,7 +907,30 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
// stack size information. Before this modification, all stack sizes in the // stack size information. Before this modification, all stack sizes in the
// section assume the default stack size (which is relatively big). // section assume the default stack size (which is relatively big).
func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map[string]functionStackSize) error { 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 { if err != nil {
return err return err
} }
@@ -955,19 +952,15 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
if fn.stackSizeType == stacksize.Bounded { if fn.stackSizeType == stacksize.Bounded {
stackSize := uint32(fn.stackSize) stackSize := uint32(fn.stackSize)
// Add stack size used by interrupts. // Adding 4 for the stack canary. Even though the size may be
switch fileHeader.Machine { // automatically determined, stack overflow checking is still
case elf.EM_ARM: // important as the stack size cannot be determined for all
if stackSize%8 != 0 { // goroutines.
// If the stack isn't a multiple of 8, it means the leaf stackSize += 4
// 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
}
// 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 // On Cortex-M (assumed here), this stack size is 8 words or 32
// bytes. This is only to store the registers that the interrupt // bytes. This is only to store the registers that the interrupt
// may modify, the interrupt will switch to the interrupt stack // may modify, the interrupt will switch to the interrupt stack
@@ -975,14 +968,6 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
// Some background: // Some background:
// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching // https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
stackSize += 32 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. // Finally write the stack size to the binary.
@@ -990,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 // printStacks prints the maximum stack depth for functions that are started as
@@ -1022,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 -3
View File
@@ -56,7 +56,7 @@ import (
// depfile but without invalidating its name. For this reason, the depfile is // depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it // 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. // 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. // Hash input file.
fileHash, err := hashFile(abspath) fileHash, err := hashFile(abspath)
if err != nil { if err != nil {
@@ -128,8 +128,8 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
// flags (for the assembler) is a compiler error. // flags (for the assembler) is a compiler error.
flags = append(flags, "-Qunused-arguments") flags = append(flags, "-Qunused-arguments")
} }
if printCommands != nil { if printCommands {
printCommands("clang", flags...) fmt.Printf("clang %s\n", strings.Join(flags, " "))
} }
err = runCCompiler(flags...) err = runCCompiler(flags...)
if err != nil { if err != nil {
-10
View File
@@ -17,18 +17,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil { if err != nil {
return nil, err 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") goroot := goenv.Get("GOROOT")
if goroot == "" { if goroot == "" {
return nil, errors.New("cannot locate $GOROOT, please set it manually") return nil, errors.New("cannot locate $GOROOT, please set it manually")
} }
major, minor, err := goenv.GetGorootVersion(goroot) major, minor, err := goenv.GetGorootVersion(goroot)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err) return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
@@ -36,9 +28,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if major != 1 || minor < 13 || minor > 16 { 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) return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
} }
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
return &compileopts.Config{ return &compileopts.Config{
Options: options, Options: options,
Target: spec, Target: spec,
-57
View File
@@ -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
}
-27
View File
@@ -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
}
+74 -164
View File
@@ -11,184 +11,105 @@ import (
"strings" "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. // parseConst parses the given string as a C constant.
func parseConst(pos token.Pos, fset *token.FileSet, value string) (ast.Expr, *scanner.Error) { func parseConst(pos token.Pos, fset *token.FileSet, value string) (ast.Expr, *scanner.Error) {
t := newTokenizer(pos, fset, value) t := newTokenizer(pos, fset, value)
expr, err := parseConstExpr(t, precedenceLowest) expr, err := parseConstExpr(t)
t.Next() if t.token != token.EOF {
if t.curToken != token.EOF {
return nil, &scanner.Error{ return nil, &scanner.Error{
Pos: t.fset.Position(t.curPos), Pos: t.fset.Position(t.pos),
Msg: "unexpected token " + t.curToken.String() + ", expected end of expression", Msg: "unexpected token " + t.token.String(),
} }
} }
return expr, err return expr, err
} }
// parseConstExpr parses a stream of C tokens to a Go expression. // parseConstExpr parses a stream of C tokens to a Go expression.
func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) { func parseConstExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
if t.curToken == token.EOF { 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{ return nil, &scanner.Error{
Pos: t.fset.Position(t.curPos), Pos: t.fset.Position(t.pos),
Msg: "empty constant", Msg: "empty constant",
} }
} default:
prefix := prefixParseFns[t.curToken]
if prefix == nil {
return nil, &scanner.Error{ return nil, &scanner.Error{
Pos: t.fset.Position(t.curPos), Pos: t.fset.Position(t.pos),
Msg: fmt.Sprintf("unexpected token %s", t.curToken), 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 // unexpectedToken returns an error of the form "unexpected token FOO, expected
// BAR". // BAR".
func unexpectedToken(t *tokenizer, expected token.Token) *scanner.Error { func unexpectedToken(t *tokenizer, expected token.Token) *scanner.Error {
return &scanner.Error{ return &scanner.Error{
Pos: t.fset.Position(t.curPos), Pos: t.fset.Position(t.pos),
Msg: fmt.Sprintf("unexpected token %s, expected %s", t.curToken, expected), Msg: fmt.Sprintf("unexpected token %s, expected %s", t.token, expected),
} }
} }
// tokenizer reads C source code and converts it to Go tokens. // tokenizer reads C source code and converts it to Go tokens.
type tokenizer struct { type tokenizer struct {
curPos, peekPos token.Pos pos token.Pos
fset *token.FileSet fset *token.FileSet
curToken, peekToken token.Token token token.Token
curValue, peekValue string value string
buf string buf string
} }
// newTokenizer initializes a new tokenizer, positioned at the first token in // newTokenizer initializes a new tokenizer, positioned at the first token in
// the string. // the string.
func newTokenizer(start token.Pos, fset *token.FileSet, buf string) *tokenizer { func newTokenizer(start token.Pos, fset *token.FileSet, buf string) *tokenizer {
t := &tokenizer{ t := &tokenizer{
peekPos: start, pos: start,
fset: fset, fset: fset,
buf: buf, buf: buf,
peekToken: token.ILLEGAL, token: token.ILLEGAL,
} }
// Parse the first two tokens (cur and peek). t.Next() // Parse the first token.
t.Next()
t.Next()
return t return t
} }
// Next consumes the next token in the stream. There is no return value, read // Next consumes the next token in the stream. There is no return value, read
// the next token from the pos, token and value properties. // the next token from the pos, token and value properties.
func (t *tokenizer) Next() { func (t *tokenizer) Next() {
// The previous peek is now the current token. t.pos += token.Pos(len(t.value))
t.curPos = t.peekPos
t.curToken = t.peekToken
t.curValue = t.peekValue
// Parse the next peek token.
t.peekPos += token.Pos(len(t.curValue))
for { for {
if len(t.buf) == 0 { if len(t.buf) == 0 {
t.peekToken = token.EOF t.token = token.EOF
return return
} }
c := t.buf[0] c := t.buf[0]
@@ -197,28 +118,17 @@ func (t *tokenizer) Next() {
// Skip whitespace. // Skip whitespace.
// Based on this source, not sure whether it represents C whitespace: // Based on this source, not sure whether it represents C whitespace:
// https://en.cppreference.com/w/cpp/string/byte/isspace // https://en.cppreference.com/w/cpp/string/byte/isspace
t.peekPos++ t.pos++
t.buf = t.buf[1:] t.buf = t.buf[1:]
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%': case c == '(' || c == ')':
// Single-character tokens. // Single-character tokens.
// TODO: ++ (increment) and -- (decrement) operators.
switch c { switch c {
case '(': case '(':
t.peekToken = token.LPAREN t.token = token.LPAREN
case ')': case ')':
t.peekToken = token.RPAREN t.token = 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.peekValue = t.buf[:1] t.value = t.buf[:1]
t.buf = t.buf[1:] t.buf = t.buf[1:]
return return
case c >= '0' && c <= '9': case c >= '0' && c <= '9':
@@ -236,17 +146,17 @@ func (t *tokenizer) Next() {
break break
} }
} }
t.peekValue = t.buf[:tokenLen] t.value = t.buf[:tokenLen]
t.buf = t.buf[tokenLen:] t.buf = t.buf[tokenLen:]
if hasDot { if hasDot {
// Integer constants are more complicated than this but this is // Integer constants are more complicated than this but this is
// a close approximation. // a close approximation.
// https://en.cppreference.com/w/cpp/language/integer_literal // https://en.cppreference.com/w/cpp/language/integer_literal
t.peekToken = token.FLOAT t.token = token.FLOAT
t.peekValue = strings.TrimRight(t.peekValue, "f") t.value = strings.TrimRight(t.value, "f")
} else { } else {
t.peekToken = token.INT t.token = token.INT
t.peekValue = strings.TrimRight(t.peekValue, "uUlL") t.value = strings.TrimRight(t.value, "uUlL")
} }
return return
case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_': case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_':
@@ -260,9 +170,9 @@ func (t *tokenizer) Next() {
break break
} }
} }
t.peekValue = t.buf[:tokenLen] t.value = t.buf[:tokenLen]
t.buf = t.buf[tokenLen:] t.buf = t.buf[tokenLen:]
t.peekToken = token.IDENT t.token = token.IDENT
return return
case c == '"': case c == '"':
// String constant. Find the first '"' character that is not // String constant. Find the first '"' character that is not
@@ -278,8 +188,8 @@ func (t *tokenizer) Next() {
escape = c == '\\' escape = c == '\\'
} }
} }
t.peekToken = token.STRING t.token = token.STRING
t.peekValue = t.buf[:tokenLen] t.value = t.buf[:tokenLen]
t.buf = t.buf[tokenLen:] t.buf = t.buf[tokenLen:]
return return
case c == '\'': case c == '\'':
@@ -296,12 +206,12 @@ func (t *tokenizer) Next() {
escape = c == '\\' escape = c == '\\'
} }
} }
t.peekToken = token.CHAR t.token = token.CHAR
t.peekValue = t.buf[:tokenLen] t.value = t.buf[:tokenLen]
t.buf = t.buf[tokenLen:] t.buf = t.buf[tokenLen:]
return return
default: default:
t.peekToken = token.ILLEGAL t.token = token.ILLEGAL
return return
} }
} }
+2 -19
View File
@@ -18,7 +18,7 @@ func TestParseConst(t *testing.T) {
{`(5)`, `(5)`}, {`(5)`, `(5)`},
{`(((5)))`, `(5)`}, {`(((5)))`, `(5)`},
{`)`, `error: 1:1: unexpected token )`}, {`)`, `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 )`}, {" \t)", `error: 1:4: unexpected token )`},
{`5.8f`, `5.8`}, {`5.8f`, `5.8`},
{`foo`, `C.foo`}, {`foo`, `C.foo`},
@@ -30,24 +30,7 @@ func TestParseConst(t *testing.T) {
{`'a'`, `'a'`}, {`'a'`, `'a'`},
{`0b10`, `0b10`}, {`0b10`, `0b10`},
{`0x1234_5678`, `0x1234_5678`}, {`0x1234_5678`, `0x1234_5678`},
{`5 5`, `error: 1:3: unexpected token INT, expected end of expression`}, // test for a bugfix {`5 5`, `error: 1:3: unexpected token INT`}, // 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`},
} { } {
fset := token.NewFileSet() fset := token.NewFileSet()
startPos := fset.AddFile("", -1, 1000).Pos(0) startPos := fset.AddFile("", -1, 1000).Pos(0)
+1 -1
View File
@@ -1,7 +1,7 @@
// CGo errors: // CGo errors:
// testdata/errors.go:4:2: warning: some warning // testdata/errors.go:4:2: warning: some warning
// testdata/errors.go:11:9: error: unknown type name 'someType' // 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: // 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) // testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
+8 -33
View File
@@ -55,7 +55,7 @@ func (c *Config) GOARCH() string {
// BuildTags returns the complete list of build tags used during this build. // BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string { func (c *Config) BuildTags() []string {
tags := append(c.Target.BuildTags, []string{"tinygo", "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++ { for i := 1; i <= c.GoMinorVersion; i++ {
tags = append(tags, fmt.Sprintf("go1.%d", i)) tags = append(tags, fmt.Sprintf("go1.%d", i))
} }
@@ -80,7 +80,12 @@ func (c *Config) GC() string {
if c.Target.GC != "" { if c.Target.GC != "" {
return 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 // NeedsStackObjects returns true if the compiler should insert stack objects
@@ -89,7 +94,7 @@ func (c *Config) NeedsStackObjects() bool {
switch c.GC() { switch c.GC() {
case "conservative", "extalloc": case "conservative", "extalloc":
for _, tag := range c.BuildTags() { for _, tag := range c.BuildTags() {
if tag == "tinygo.wasm" { if tag == "wasm" {
return true return true
} }
} }
@@ -113,18 +118,6 @@ func (c *Config) Scheduler() string {
return "coroutines" 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 // OptLevels returns the optimization level (0-2), size level (0-2), and inliner
// threshold as used in the LLVM optimization pipeline. // threshold as used in the LLVM optimization pipeline.
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) { func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
@@ -188,15 +181,6 @@ func (c *Config) AutomaticStackSize() bool {
return false 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 // CFlags returns the flags to pass to the C compiler. This is necessary for CGo
// preprocessing. // preprocessing.
func (c *Config) CFlags() []string { func (c *Config) CFlags() []string {
@@ -275,11 +259,6 @@ func (c *Config) BinaryFormat(ext string) string {
// More information: // More information:
// https://github.com/Microsoft/uf2 // https://github.com/Microsoft/uf2
return "uf2" return "uf2"
case ".zip":
if c.Target.BinaryFormat != "" {
return c.Target.BinaryFormat
}
return "zip"
default: default:
// Use the ELF format for unrecognized file formats. // Use the ELF format for unrecognized file formats.
return "elf" return "elf"
@@ -363,10 +342,6 @@ func (c *Config) WasmAbi() string {
return c.Target.WasmAbi return c.Target.WasmAbi
} }
func (c *Config) LLVMFeatures() string {
return c.Options.LLVMFeatures
}
type TestConfig struct { type TestConfig struct {
CompileTestBinary bool CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc // TODO: Filter the test functions to run, include verbose flag, etc
+18 -31
View File
@@ -9,7 +9,6 @@ import (
var ( var (
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"} validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
validSchedulerOptions = []string{"none", "tasks", "coroutines"} validSchedulerOptions = []string{"none", "tasks", "coroutines"}
validSerialOptions = []string{"none", "uart", "usb"}
validPrintSizeOptions = []string{"none", "short", "full"} validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"} validPanicStrategyOptions = []string{"print", "trap"}
validOptOptions = []string{"none", "0", "1", "2", "s", "z"} 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 // Options contains extra options to give to the compiler. These options are
// usually passed from the command line. // usually passed from the command line.
type Options struct { type Options struct {
Target string Target string
Opt string Opt string
GC string GC string
PanicStrategy string PanicStrategy string
Scheduler string Scheduler string
Serial string PrintIR bool
PrintIR bool DumpSSA bool
DumpSSA bool VerifyIR bool
VerifyIR bool PrintCommands bool
PrintCommands func(cmd string, args ...string) Debug bool
Debug bool PrintSizes string
PrintSizes string PrintAllocs *regexp.Regexp // regexp string
PrintAllocs *regexp.Regexp // regexp string PrintStacks bool
PrintStacks bool Tags string
Tags string WasmAbi string
WasmAbi string GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value TestConfig TestConfig
TestConfig TestConfig Programmer string
Programmer string
OpenOCDCommands []string
LLVMFeatures string
} }
// Verify performs a validation on the given options, raising an error if options are not valid. // 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 != "" { if o.PrintSizes != "" {
valid := isInArray(validPrintSizeOptions, o.PrintSizes) valid := isInArray(validPrintSizeOptions, o.PrintSizes)
if !valid { if !valid {
+8 -15
View File
@@ -31,7 +31,6 @@ type TargetSpec struct {
BuildTags []string `json:"build-tags"` BuildTags []string `json:"build-tags"`
GC string `json:"gc"` GC string `json:"gc"`
Scheduler string `json:"scheduler"` Scheduler string `json:"scheduler"`
Serial string `json:"serial"` // which serial output to use (uart, usb, none)
Linker string `json:"linker"` Linker string `json:"linker"`
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt) RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
Libc string `json:"libc"` Libc string `json:"libc"`
@@ -41,12 +40,10 @@ type TargetSpec struct {
LDFlags []string `json:"ldflags"` LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"` LinkerScript string `json:"linkerscript"`
ExtraFiles []string `json:"extra-files"` 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 Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
FlashCommand string `json:"flash-command"` FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"` GDB []string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"` 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"` FlashMethod string `json:"flash-method"`
FlashVolume string `json:"msd-volume-name"` FlashVolume string `json:"msd-volume-name"`
FlashFilename string `json:"msd-firmware-name"` FlashFilename string `json:"msd-firmware-name"`
@@ -242,26 +239,22 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// No target spec available. Use the default one, useful on most systems // No target spec available. Use the default one, useful on most systems
// with a regular OS. // with a regular OS.
spec := TargetSpec{ spec := TargetSpec{
Triple: triple, Triple: triple,
GOOS: goos, GOOS: goos,
GOARCH: goarch, GOARCH: goarch,
BuildTags: []string{goos, goarch}, BuildTags: []string{goos, goarch},
Scheduler: "tasks", Linker: "cc",
Linker: "cc", CFlags: []string{"--target=" + triple},
DefaultStackSize: 1024 * 64, // 64kB GDB: []string{"gdb"},
CFlags: []string{"--target=" + triple}, PortReset: "false",
GDB: []string{"gdb"},
PortReset: "false",
} }
if goos == "darwin" { if goos == "darwin" {
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip") spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
} else { } else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
} }
if goarch != "wasm" { if goarch != "wasm" {
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S") 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 { if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs. // Some educated guesses as to how to invoke helper programs.
-4
View File
@@ -162,10 +162,6 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str
case *ssa.Alloc: case *ssa.Alloc:
// An alloc is never nil. // An alloc is never nil.
return return
case *ssa.FreeVar:
// A free variable is allocated in a parent function and is thus never
// nil.
return
case *ssa.IndexAddr: case *ssa.IndexAddr:
// This pointer is the result of an index operation into a slice or // This pointer is the result of an index operation into a slice or
// array. Such slices/arrays are already bounds checked so the pointer // array. Such slices/arrays are already bounds checked so the pointer
+51 -102
View File
@@ -23,7 +23,7 @@ import (
// Version of the compiler pacakge. Must be incremented each time the compiler // Version of the compiler pacakge. Must be incremented each time the compiler
// package changes in a way that affects the generated LLVM module. // package changes in a way that affects the generated LLVM module.
// This version is independent of the TinyGo version number. // This version is independent of the TinyGo version number.
const Version = 12 // last change: implement syscall.rawSyscallNoError const Version = 9 // last change: implement reflect.New()
func init() { func init() {
llvm.InitializeAllTargets() llvm.InitializeAllTargets()
@@ -59,7 +59,6 @@ type Config struct {
DefaultStackSize uint64 DefaultStackSize uint64
NeedsStackObjects bool NeedsStackObjects bool
Debug bool // Whether to emit debug information in the LLVM module. Debug bool // Whether to emit debug information in the LLVM module.
LLVMFeatures string
} }
// compilerContext contains function-independent data that should still be // compilerContext contains function-independent data that should still be
@@ -186,12 +185,7 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
if err != nil { if err != nil {
return llvm.TargetMachine{}, err return llvm.TargetMachine{}, err
} }
features := strings.Join(config.Features, ",")
feat := config.Features
if len(config.LLVMFeatures) > 0 {
feat = append(feat, config.LLVMFeatures)
}
features := strings.Join(feat, ",")
var codeModel llvm.CodeModel var codeModel llvm.CodeModel
var relocationModel llvm.RelocMode var relocationModel llvm.RelocMode
@@ -693,7 +687,6 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
// createPackage builds the LLVM IR for all types, methods, and global variables // createPackage builds the LLVM IR for all types, methods, and global variables
// in the given package. // in the given package.
func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) { func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
println("called create package:", pkg.Pkg.Name())
// Sort by position, so that the order of the functions in the IR matches // Sort by position, so that the order of the functions in the IR matches
// the order of functions in the source file. This is useful for testing, // the order of functions in the source file. This is useful for testing,
// for example. // for example.
@@ -716,11 +709,11 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
member := pkg.Members[name] member := pkg.Members[name]
switch member := member.(type) { switch member := member.(type) {
case *ssa.Function: case *ssa.Function:
// Create the function definition.
b := newBuilder(c, irbuilder, member)
if member.Blocks == nil { if member.Blocks == nil {
continue // external function continue // external function
} }
// Create the function definition.
b := newBuilder(c, irbuilder, member)
b.createFunction() b.createFunction()
case *ssa.Type: case *ssa.Type:
if types.IsInterface(member.Type()) { if types.IsInterface(member.Type()) {
@@ -759,44 +752,11 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
case *ssa.Global: case *ssa.Global:
// Global variable. // Global variable.
info := c.getGlobalInfo(member) info := c.getGlobalInfo(member)
// process go:embed pragmas
// TODO: skip this if not Go 1.16 or higher
if strings.TrimSpace(info.embeds) != "" {
embeds, err := parseGoEmbed(info.embeds)
if err != nil {
c.addError(member.Pos(), `invalid go:embed pragma: `+err.Error())
goto getGlobal
}
importsEmbedPkg := false
for _, imprt := range pkg.Pkg.Imports() {
if imprt.Name() == "embed" {
importsEmbedPkg = true
break
}
}
if !importsEmbedPkg {
// FIXME: technically the "embed" package needs to be imported in every
// *source file* that has a //go:embed pragma... that will always be the
// case when referencing embed.FS but not necessarily when a go:embed
// pragma is added to a string or []byte. For now, as long as the embed
// package is imported in at least one source file, TinyGo will process
// any go:embed pragmas in the entire package.
c.addError(member.Pos(), `//go:embed only allowed in Go files that import "embed"`)
goto getGlobal
}
fmt.Printf("file embed found at %v (import: %v): %v\n", info.linkName, importsEmbedPkg, embeds)
}
getGlobal:
global := c.getGlobal(member)
if !info.extern { if !info.extern {
global := c.getGlobal(member)
global.SetInitializer(llvm.ConstNull(global.Type().ElementType())) global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
global.SetVisibility(llvm.HiddenVisibility) global.SetVisibility(llvm.HiddenVisibility)
if info.section != "" {
global.SetSection(info.section)
}
} }
} }
} }
} }
@@ -821,9 +781,6 @@ func (b *builder) createFunction() {
b.llvmFn.SetVisibility(llvm.HiddenVisibility) b.llvmFn.SetVisibility(llvm.HiddenVisibility)
b.llvmFn.SetUnnamedAddr(true) b.llvmFn.SetUnnamedAddr(true)
} }
if b.info.section != "" {
b.llvmFn.SetSection(b.info.section)
}
if b.info.exported && strings.HasPrefix(b.Triple, "wasm") { if b.info.exported && strings.HasPrefix(b.Triple, "wasm") {
// Set the exported name. This is necessary for WebAssembly because // Set the exported name. This is necessary for WebAssembly because
// otherwise the function is not exported. // otherwise the function is not exported.
@@ -1007,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 // createInstruction builds the LLVM IR equivalent instructions for the
// particular Go SSA instruction. // particular Go SSA instruction.
func (b *builder) createInstruction(instr ssa.Instruction) { func (b *builder) createInstruction(instr ssa.Instruction) {
if b.Debug { 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{}) b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
} }
@@ -1087,8 +994,52 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
case *ssa.Defer: case *ssa.Defer:
b.createDefer(instr) b.createDefer(instr)
case *ssa.Go: 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. // 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: case *ssa.If:
cond := b.getValue(instr.Cond) cond := b.getValue(instr.Cond)
block := instr.Block() block := instr.Block()
@@ -1341,8 +1292,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.emitCSROperation(instr) return b.emitCSROperation(instr)
case strings.HasPrefix(name, "syscall.Syscall"): case strings.HasPrefix(name, "syscall.Syscall"):
return b.createSyscall(instr) return b.createSyscall(instr)
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
return b.createRawSyscallNoError(instr)
case strings.HasPrefix(name, "runtime/volatile.Load"): case strings.HasPrefix(name, "runtime/volatile.Load"):
return b.createVolatileLoad(instr) return b.createVolatileLoad(instr)
case strings.HasPrefix(name, "runtime/volatile.Store"): case strings.HasPrefix(name, "runtime/volatile.Store"):
+38 -57
View File
@@ -19,8 +19,6 @@ var flagUpdate = flag.Bool("update", false, "update tests based on test output")
// Basic tests for the compiler. Build some Go files and compare the output with // Basic tests for the compiler. Build some Go files and compare the output with
// the expected LLVM IR for regression testing. // the expected LLVM IR for regression testing.
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
t.Parallel()
// Check LLVM version. // Check LLVM version.
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0]) llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil { if err != nil {
@@ -34,56 +32,43 @@ func TestCompiler(t *testing.T) {
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version) t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
} }
tests := []struct { target, err := compileopts.LoadTarget("i686--linux")
file string if err != nil {
target string t.Fatal("failed to load target:", err)
}{ }
{"basic.go", ""}, config := &compileopts.Config{
{"pointer.go", ""}, Options: &compileopts.Options{},
{"slice.go", ""}, Target: target,
{"string.go", ""}, }
{"float.go", ""}, compilerConfig := &Config{
{"interface.go", ""}, Triple: config.Triple(),
{"func.go", ""}, GOOS: config.GOOS(),
{"pragma.go", ""}, GOARCH: config.GOARCH(),
{"goroutine.go", "wasm"}, CodeModel: config.CodeModel(),
{"goroutine.go", "cortex-m-qemu"}, 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 { tests := []string{
name := tc.file "basic.go",
targetString := "wasm" "pointer.go",
if tc.target != "" { "slice.go",
targetString = tc.target "string.go",
name = tc.file + "-" + tc.target "float.go",
} "interface.go",
"func.go",
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)
}
for _, testCase := range tests {
t.Run(testCase, func(t *testing.T) {
// Load entire program AST into memory. // 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), Sizes: Sizes(machine),
}) })
if err != nil { if err != nil {
@@ -91,13 +76,13 @@ func TestCompiler(t *testing.T) {
} }
err = lprogram.Parse() err = lprogram.Parse()
if err != nil { 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. // Compile AST to IR.
program := lprogram.LoadSSA() program := lprogram.LoadSSA()
pkg := lprogram.MainPkg() 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 { if errs != nil {
for _, err := range errs { for _, err := range errs {
t.Error(err) t.Error(err)
@@ -120,22 +105,18 @@ func TestCompiler(t *testing.T) {
} }
funcPasses.FinalizeFunc() funcPasses.FinalizeFunc()
outFilePrefix := tc.file[:len(tc.file)-3] outfile := "./testdata/" + testCase[:len(testCase)-3] + ".ll"
if tc.target != "" {
outFilePrefix += "-" + tc.target
}
outPath := "./testdata/" + outFilePrefix + ".ll"
// Update test if needed. Do not check the result. // Update test if needed. Do not check the result.
if *flagUpdate { if *flagUpdate {
err := ioutil.WriteFile(outPath, []byte(mod.String()), 0666) err := ioutil.WriteFile(outfile, []byte(mod.String()), 0666)
if err != nil { if err != nil {
t.Error("failed to write updated output file:", err) t.Error("failed to write updated output file:", err)
} }
return return
} }
expected, err := ioutil.ReadFile(outPath) expected, err := ioutil.ReadFile(outfile)
if err != nil { if err != nil {
t.Fatal("failed to read golden file:", err) t.Fatal("failed to read golden file:", err)
} }
-70
View File
@@ -1,70 +0,0 @@
package compiler
import (
"fmt"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
// Copied from https://github.com/golang/go/blob/219fe9d547d09d3de1b024c6c8b8314dd0bf12e4/src/cmd/compile/internal/noder/noder.go#L1717-L1776
func parseGoEmbed(args string) ([]string, error) {
var list []string
for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
var path string
Switch:
switch args[0] {
default:
i := len(args)
for j, c := range args {
if unicode.IsSpace(c) {
i = j
break
}
}
path = args[:i]
args = args[i:]
case '`':
i := strings.Index(args[1:], "`")
if i < 0 {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
}
path = args[1 : 1+i]
args = args[1+i+1:]
case '"':
i := 1
for ; i < len(args); i++ {
if args[i] == '\\' {
i++
continue
}
if args[i] == '"' {
q, err := strconv.Unquote(args[:i+1])
if err != nil {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
}
path = q
args = args[i+1:]
break Switch
}
}
if i >= len(args) {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
}
}
if args != "" {
r, _ := utf8.DecodeRuneInString(args)
if !unicode.IsSpace(r) {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
}
}
list = append(list, path)
}
return list, nil
}
+15 -117
View File
@@ -5,110 +5,25 @@ package compiler
import ( import (
"go/token" "go/token"
"go/types"
"github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// createGo emits code to start a new goroutine. // createGoInstruction starts a new goroutine with the provided function pointer
func (b *builder) createGo(instr *ssa.Go) { // and parameters.
// Get all function parameters to pass to the goroutine. // In general, you should pass all regular parameters plus the context parameter.
var params []llvm.Value // There is one exception: the task-based scheduler needs to have the function
for _, param := range instr.Call.Args { // pointer passed in as a parameter too in addition to the context.
params = append(params, b.getValue(param)) //
} // 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 {
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
}
paramBundle := b.emitPointerPack(params) paramBundle := b.emitPointerPack(params)
var callee, stackSize llvm.Value var callee, stackSize llvm.Value
switch b.Scheduler { switch b.Scheduler {
case "none", "tasks": case "none", "tasks":
callee = b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos()) callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
if b.AutomaticStackSize { if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy // The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF // function that will be replaced with a load from a special ELF
@@ -119,9 +34,6 @@ func (b *builder) createGo(instr *ssa.Go) {
} else { } else {
// The stack size is fixed at compile time. By emitting it here as a // The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized. // 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) stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
} }
case "coroutines": 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)) 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)}, "") 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 // 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 // allows a single (pointer) argument to the newly started goroutine. Also, it
// ignores the return value because newly started goroutines do not have a // ignores the return value because newly started goroutines do not have a
// return value. // return value.
// func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix string, pos token.Pos) llvm.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 {
var wrapper llvm.Value var wrapper llvm.Value
builder := c.ctx.NewBuilder() 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. // Create the list of params for the call.
paramTypes := fn.Type().ElementType().ParamTypes() paramTypes := fn.Type().ElementType().ParamTypes()
paramTypes = paramTypes[:len(paramTypes)-1] // strip parentHandle parameter params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes[:len(paramTypes)-1])
if !hasContext { params = append(params, llvm.Undef(c.i8ptrType))
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
// Create the call. // Create the call.
builder.CreateCall(fn, params, "") builder.CreateCall(fn, params, "")
@@ -276,11 +177,8 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
// Get the function pointer. // Get the function pointer.
fnPtr := params[len(params)-1] fnPtr := params[len(params)-1]
// The last parameter in the packed object has somewhat of a dual role. // Ignore the last param, which isn't used anymore.
// Inside the parameter bundle it's the function pointer, stored right // TODO: avoid this extra "parent handle" parameter in most functions.
// 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.
params[len(params)-1] = llvm.Undef(c.i8ptrType) params[len(params)-1] = llvm.Undef(c.i8ptrType)
// Create the call. // Create the call.
+2 -13
View File
@@ -311,21 +311,10 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
// used during the interface lowering pass. // used during the interface lowering pass.
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value { func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
signature := methodSignature(method) signature := methodSignature(method)
var globalName string signatureGlobal := c.mod.NamedGlobal("func " + signature)
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)
if signatureGlobal.IsNil() { if signatureGlobal.IsNil() {
// TODO: put something useful in these globals, such as the method signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature)
// 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.SetGlobalConstant(true) signatureGlobal.SetGlobalConstant(true)
signatureGlobal.SetAlignment(1)
} }
return signatureGlobal return signatureGlobal
} }
+4 -1
View File
@@ -41,7 +41,10 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// type are lowered in the interrupt lowering pass. // type are lowered in the interrupt lowering pass.
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type() globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
globalLLVMType := b.getLLVMType(globalType) 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 := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
global.SetVisibility(llvm.HiddenVisibility) global.SetVisibility(llvm.HiddenVisibility)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
-14
View File
@@ -24,7 +24,6 @@ type functionInfo struct {
module string // go:wasm-module module string // go:wasm-module
importName string // go:linkname, go:export - The name the developer assigns 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 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 exported bool // go:export, CGo
nobounds bool // go:nobounds nobounds bool // go:nobounds
variadic bool // go:variadic (CGo only) variadic bool // go:variadic (CGo only)
@@ -271,10 +270,6 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
if hasUnsafeImport(f.Pkg.Pkg) { if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2] info.linkName = parts[2]
} }
case "//go:section":
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
info.section = parts[1]
}
case "//go:nobounds": case "//go:nobounds":
// Skip bounds checking in this function. Useful for some // Skip bounds checking in this function. Useful for some
// runtime functions. // runtime functions.
@@ -330,8 +325,6 @@ type globalInfo struct {
linkName string // go:extern linkName string // go:extern
extern bool // go:extern extern bool // go:extern
align int // go:align align int // go:align
section string // go:section
embeds string // go:embed
} }
// loadASTComments loads comments on globals from the AST, for use later in the // loadASTComments loads comments on globals from the AST, for use later in the
@@ -445,13 +438,6 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
if err == nil { if err == nil {
info.align = align info.align = align
} }
case "//go:section":
if len(parts) == 2 {
info.section = parts[1]
}
case "//go:embed":
raw := strings.TrimPrefix(comment.Text, "//go:embed")
info.embeds += " " + raw
} }
} }
} }
+8 -30
View File
@@ -10,11 +10,11 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// createRawSyscall creates a system call with the provided system call number // createSyscall emits an inline system call instruction, depending on the
// and returns the result as a single integer (the system call result). The // target OS/arch.
// result is not further interpreted. func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
num := b.getValue(call.Args[0]) num := b.getValue(call.Args[0])
var syscallResult llvm.Value
switch { switch {
case b.GOARCH == "amd64": case b.GOARCH == "amd64":
if b.GOOS == "darwin" { if b.GOOS == "darwin" {
@@ -57,7 +57,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
constraints += ",~{rcx},~{r11}" constraints += ",~{rcx},~{r11}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) 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": case b.GOARCH == "386" && b.GOOS == "linux":
// Sources: // Sources:
// syscall(2) man page // 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) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel) 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": case b.GOARCH == "arm" && b.GOOS == "linux":
// Implement the EABI system call convention for Linux. // Implement the EABI system call convention for Linux.
// Source: syscall(2) man page. // 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) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) 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": case b.GOARCH == "arm64" && b.GOOS == "linux":
// Source: syscall(2) man page. // Source: syscall(2) man page.
args := []llvm.Value{} args := []llvm.Value{}
@@ -147,19 +147,10 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
constraints += ",~{x16},~{x17}" // scratch registers constraints += ",~{x16},~{x17}" // scratch registers
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
return b.CreateCall(target, args, ""), nil syscallResult = b.CreateCall(target, args, "")
default: default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH) 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 { switch b.GOOS {
case "linux", "freebsd": case "linux", "freebsd":
// Return values: r0, r1 uintptr, err Errno // 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) 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
}
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'basic.go' ; ModuleID = 'basic.go'
source_filename = "basic.go" source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'float.go' ; ModuleID = 'float.go'
source_filename = "float.go" source_filename = "float.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'func.go' ; ModuleID = 'func.go'
source_filename = "func.go" source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 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 = "wasm32--wasi" target triple = "i686--linux"
%runtime.funcValueWithSignature = type { i32, i8* } %runtime.funcValueWithSignature = type { i32, i8* }
-150
View File
@@ -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" }
-106
View File
@@ -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*)
-43
View File
@@ -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)
+8 -8
View File
@@ -1,7 +1,7 @@
; ModuleID = 'interface.go' ; ModuleID = 'interface.go'
source_filename = "interface.go" source_filename = "interface.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 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 = "wasm32--wasi" target triple = "i686--linux"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
%runtime.interfaceMethodInfo = type { i8*, i32 } %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: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: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/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 @"func Error() string" = external constant i8
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"] @"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:{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: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/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 @"func String() string" = external constant i8
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"] @"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 @"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*) 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 { define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
entry: 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*)* %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) %0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef)
ret %runtime._string %0 ret %runtime._string %0
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'pointer.go' ; ModuleID = 'pointer.go'
source_filename = "pointer.go" source_filename = "pointer.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
-66
View File
@@ -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
-59
View File
@@ -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" }
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'slice.go' ; ModuleID = 'slice.go'
source_filename = "slice.go" source_filename = "slice.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 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 = "wasm32--wasi" target triple = "i686--linux"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+2 -2
View File
@@ -1,7 +1,7 @@
; ModuleID = 'string.go' ; ModuleID = 'string.go'
source_filename = "string.go" source_filename = "string.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 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 = "wasm32--wasi" target triple = "i686--linux"
%runtime._string = type { i8*, i32 } %runtime._string = type { i8*, i32 }
+1 -1
View File
@@ -1,6 +1,6 @@
module github.com/tinygo-org/tinygo module github.com/tinygo-org/tinygo
go 1.16 go 1.13
require ( require (
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo. // Version of TinyGo.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const Version = "0.20.0-dev" const Version = "0.18.0-dev"
// GetGorootVersion returns the major and minor version for a given GOROOT path. // GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned. // If the goroot cannot be determined, (0, 0) is returned.
-1
View File
@@ -57,7 +57,6 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
pos := getPosition(inst.llvmInst) pos := getPosition(inst.llvmInst)
return &Error{ return &Error{
ImportPath: r.pkgName, ImportPath: r.pkgName,
Inst: inst.llvmInst,
Pos: pos, Pos: pos,
Err: err, Err: err,
Traceback: []ErrorLine{{pos, inst.llvmInst}}, Traceback: []ErrorLine{{pos, inst.llvmInst}},
+3 -34
View File
@@ -15,7 +15,7 @@ import (
// package is changed in a way that affects the output so that cached package // package is changed in a way that affects the output so that cached package
// builds will be invalidated. // builds will be invalidated.
// This version is independent of the TinyGo version number. // 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. // Enable extra checks, which should be disabled by default.
// This may help track down bugs by adding a few more sanity checks. // 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()) fmt.Fprintln(os.Stderr, "call:", fn.Name())
} }
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ") _, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
call.EraseFromParentAsInstruction()
if callErr != nil { if callErr != nil {
if isRecoverableError(callErr.Err) { if isRecoverableError(callErr.Err) {
if r.debug { if r.debug {
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error()) 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() 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 continue
} }
return callErr return callErr
} }
call.EraseFromParentAsInstruction()
for index, obj := range mem.objects { for index, obj := range mem.objects {
r.objects[index] = obj r.objects[index] = obj
} }
@@ -138,7 +129,7 @@ func Run(mod llvm.Module, debug bool) error {
// Update all global variables in the LLVM module. // Update all global variables in the LLVM module.
mem := memoryView{r: r} mem := memoryView{r: r}
for i, obj := range r.objects { for _, obj := range r.objects {
if obj.llvmGlobal.IsNil() { if obj.llvmGlobal.IsNil() {
continue continue
} }
@@ -168,12 +159,6 @@ func Run(mod llvm.Module, debug bool) error {
name := obj.llvmGlobal.Name() name := obj.llvmGlobal.Name()
obj.llvmGlobal.EraseFromParentAsGlobal() obj.llvmGlobal.EraseFromParentAsGlobal()
newGlobal.SetName(name) newGlobal.SetName(name)
// Update interp-internal references.
delete(r.globals, obj.llvmGlobal)
obj.llvmGlobal = newGlobal
r.globals[newGlobal] = i
r.objects[i] = obj
continue continue
} }
if err != nil { if err != nil {
@@ -279,19 +264,3 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
r.functionCache[llvmFn] = fn r.functionCache[llvmFn] = fn
return 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
}
}
}
-1
View File
@@ -17,7 +17,6 @@ func TestInterp(t *testing.T) {
"slice-copy", "slice-copy",
"consteval", "consteval",
"interface", "interface",
"revert",
} { } {
name := name // make tc local to this closure name := name // make tc local to this closure
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
+6 -22
View File
@@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"math" "math"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@@ -931,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) result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
case llvm.ExtractValue: case llvm.ExtractValue:
indices := inst.llvmInst.Indices() indices := inst.llvmInst.Indices()
// Note: the Go LLVM API doesn't support multiple indices, so simulate if len(indices) != 1 {
// this operation with some extra extractvalue instructions. Hopefully panic("expected exactly one index")
// 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")
} }
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: case llvm.InsertValue:
indices := inst.llvmInst.Indices() indices := inst.llvmInst.Indices()
// Similar to extractvalue, we're working around a limitation in the Go if len(indices) != 1 {
// LLVM API here by splitting the insertvalue into multiple instructions panic("expected exactly one index")
// 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)
} }
result = operands[1] result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
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))
}
case llvm.Add: case llvm.Add:
result = r.builder.CreateAdd(operands[0], operands[1], inst.name) result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
case llvm.Sub: case llvm.Sub:
-14
View File
@@ -178,9 +178,6 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
default: default:
panic("interp: unknown constant expression") 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 { } else {
llvmType := llvmValue.Type() llvmType := llvmValue.Type()
switch llvmType.TypeKind() { switch llvmType.TypeKind() {
@@ -572,17 +569,6 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
} }
if llvmType.IsNil() { 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 return llvmValue, nil
} }
-22
View File
@@ -8,7 +8,6 @@ target triple = "x86_64--linux"
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0 @main.exposedValue1 = global i16 0
@main.exposedValue2 = global i16 0 @main.exposedValue2 = global i16 0
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
declare void @runtime.printint64(i64) unnamed_addr declare void @runtime.printint64(i64) unnamed_addr
@@ -66,22 +65,12 @@ entry:
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*)) call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2 store i16 7, i16* @main.exposedValue2
; Test that inline assembly is ignored.
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
; Test switch statement. ; Test switch statement.
%switch1 = call i64 @testSwitch(i64 1) ; 1 returns 6 %switch1 = call i64 @testSwitch(i64 1) ; 1 returns 6
%switch2 = call i64 @testSwitch(i64 9) ; 9 returns the default value -1 %switch2 = call i64 @testSwitch(i64 9) ; 9 returns the default value -1
call void @runtime.printint64(i64 %switch1) call void @runtime.printint64(i64 %switch1)
call void @runtime.printint64(i64 %switch2) 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 ret void
} }
@@ -105,15 +94,6 @@ entry:
ret void 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) { define i64 @testSwitch(i64 %val) {
entry: entry:
; Test switch statement. ; Test switch statement.
@@ -132,5 +112,3 @@ two:
otherwise: otherwise:
ret i64 -1 ret i64 -1
} }
declare {i8, i32, {float, {i64, i16}}} @nestedStruct()
-21
View File
@@ -7,7 +7,6 @@ target triple = "x86_64--linux"
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0 @main.exposedValue1 = global i16 0
@main.exposedValue2 = local_unnamed_addr 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 declare void @runtime.printint64(i64) unnamed_addr
@@ -26,20 +25,8 @@ entry:
store i16 5, i16* @main.exposedValue1 store i16 5, i16* @main.exposedValue1
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*)) call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2 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 6)
call void @runtime.printint64(i64 -1) 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 ret void
} }
@@ -60,12 +47,6 @@ entry:
ret void ret void
} }
define void @hasInlineAsm() {
entry:
call void asm sideeffect "", ""()
ret void
}
define i64 @testSwitch(i64 %val) local_unnamed_addr { define i64 @testSwitch(i64 %val) local_unnamed_addr {
entry: entry:
switch i64 %val, label %otherwise [ switch i64 %val, label %otherwise [
@@ -86,5 +67,3 @@ two: ; preds = %entry
otherwise: ; preds = %entry otherwise: ; preds = %entry
ret i64 -1 ret i64 -1
} }
declare { i8, i32, { float, { i64, i16 } } } @nestedStruct() local_unnamed_addr
-32
View File
@@ -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
}
-21
View File
@@ -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
}
-10
View File
@@ -23,13 +23,3 @@ type Error struct {
func (e Error) Error() string { func (e Error) Error() string {
return e.Err.Error() 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"
}
+15 -37
View File
@@ -2,14 +2,6 @@ package loader
// This file constructs a new temporary GOROOT directory by merging both the // This file constructs a new temporary GOROOT directory by merging both the
// standard Go GOROOT and the GOROOT from TinyGo using symlinks. // 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 ( import (
"crypto/sha512" "crypto/sha512"
@@ -147,7 +139,6 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
if err != nil { if err != nil {
return err return err
} }
hasTinyGoFiles := false
for _, e := range tinygoEntries { for _, e := range tinygoEntries {
if e.IsDir() { if e.IsDir() {
// A directory, so merge this thing. // A directory, so merge this thing.
@@ -163,7 +154,6 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
if err != nil { if err != nil {
return err return err
} }
hasTinyGoFiles = true
} }
} }
@@ -174,30 +164,21 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
return err return err
} }
for _, e := range gorootEntries { for _, e := range gorootEntries {
if e.IsDir() { if !e.IsDir() {
if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok { // Don't merge in files from Go. Otherwise we'd end up with a
// Already included above, so don't bother trying to create this // weird syscall package with files from both roots.
// symlink. continue
continue }
} if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok {
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name()) // Already included above, so don't bother trying to create this
oldname := filepath.Join(goroot, "src", importPath, e.Name()) // symlink.
err := symlink(oldname, newname) continue
if err != nil { }
return err newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
} oldname := filepath.Join(goroot, "src", importPath, e.Name())
} else { err := symlink(oldname, newname)
// Only merge files from Go if TinyGo does not have any files. if err != nil {
// Otherwise we'd end up with a weird mix from both Go return err
// 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
}
}
} }
} }
} }
@@ -220,8 +201,6 @@ func needsSyscallPackage(buildTags []string) bool {
func pathsToOverride(needsSyscallPackage bool) map[string]bool { func pathsToOverride(needsSyscallPackage bool) map[string]bool {
paths := map[string]bool{ paths := map[string]bool{
"/": true, "/": true,
"crypto/": true,
"crypto/rand/": false,
"device/": false, "device/": false,
"examples/": false, "examples/": false,
"internal/": true, "internal/": true,
@@ -229,7 +208,6 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
"internal/reflectlite/": false, "internal/reflectlite/": false,
"internal/task/": false, "internal/task/": false,
"machine/": false, "machine/": false,
"net/": true,
"os/": true, "os/": true,
"reflect/": false, "reflect/": false,
"runtime/": false, "runtime/": false,
+6 -11
View File
@@ -210,12 +210,6 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
p.Packages[pkg.ImportPath] = pkg 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 return p, nil
} }
@@ -343,11 +337,12 @@ func (p *Package) Check() error {
checker.Importer = p checker.Importer = p
packageName := p.ImportPath packageName := p.ImportPath
if p == p.program.MainPkg() { if p.Name == "main" {
if p.Name != "main" { // The main package normally has a different import path, such as
// Sanity check. Should not ever trigger. // "command-line-arguments" or "./testdata/cgo". Therefore, use the name
panic("expected main package to have name 'main'") // "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" packageName = "main"
} }
typesPkg, err := checker.Check(packageName, p.program.fset, p.Files, &p.info) typesPkg, err := checker.Check(packageName, p.program.fset, p.Files, &p.info)
+98 -241
View File
@@ -15,7 +15,6 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -96,31 +95,12 @@ func copyFile(src, dst string) error {
// executeCommand is a simple wrapper to exec.Cmd // executeCommand is a simple wrapper to exec.Cmd
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd { func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
if options.PrintCommands != nil { if options.PrintCommands {
options.PrintCommands(name, arg...) fmt.Printf("%s %s\n", name, strings.Join(arg, " "))
} }
return exec.Command(name, 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. // Build compiles and links the given package and writes it to outpath.
func Build(pkgName, outpath string, options *compileopts.Options) error { func Build(pkgName, outpath string, options *compileopts.Options) error {
config, err := builder.NewConfig(options) config, err := builder.NewConfig(options)
@@ -156,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 // Test runs the tests in the given package.
// possibly an error if the test failed to run. func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) error {
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) (bool, error) {
options.TestConfig.CompileTestBinary = true options.TestConfig.CompileTestBinary = true
config, err := builder.NewConfig(options) config, err := builder.NewConfig(options)
if err != nil { if err != nil {
return false, err return err
} }
passed := true return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
if testCompileOnly || outpath != "" { if testCompileOnly || outpath != "" {
// Write test binary to the specified file name. // Write test binary to the specified file name.
if outpath == "" { if outpath == "" {
@@ -180,78 +158,48 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
// Do not run the test. // Do not run the test.
return nil return nil
} }
if len(config.Target.Emulator) == 0 {
// Run the test. // Run directly.
start := time.Now() cmd := executeCommand(config.Options, result.Binary)
var err error cmd.Stdout = os.Stdout
passed, err = runPackageTest(config, result) cmd.Stderr = os.Stderr
if err != nil { cmd.Dir = result.MainDir
return err err := cmd.Run()
} if err != nil {
duration := time.Since(start) // Propagate the exit code
if err, ok := err.(*exec.ExitError); ok {
// Print the result. os.Exit(err.ExitCode())
importPath := strings.TrimSuffix(result.ImportPath, ".test") }
if passed { return &commandError{"failed to run compiled binary", result.Binary, err}
fmt.Printf("ok \t%s\t%.3fs\n", importPath, duration.Seconds()) }
return nil
} else { } 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) (bool, error) {
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 {
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.
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. // Flash builds and flashes the built binary to the given serial port.
@@ -276,8 +224,6 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
fileExt = ".bin" fileExt = ".bin"
case strings.Contains(config.Target.FlashCommand, "{uf2}"): case strings.Contains(config.Target.FlashCommand, "{uf2}"):
fileExt = ".uf2" fileExt = ".uf2"
case strings.Contains(config.Target.FlashCommand, "{zip}"):
fileExt = ".zip"
default: default:
return errors.New("invalid target file - did you forget the {hex} token in the 'flash-command' section?") return errors.New("invalid target file - did you forget the {hex} token in the 'flash-command' section?")
} }
@@ -297,7 +243,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error { return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// do we need port reset to put MCU into bootloader mode? // do we need port reset to put MCU into bootloader mode?
if config.Target.PortReset == "true" && flashMethod != "openocd" { 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 { if err != nil {
return err return err
} }
@@ -315,36 +261,36 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
case "", "command": case "", "command":
// Create the command. // Create the command.
flashCmd := config.Target.FlashCommand flashCmd := config.Target.FlashCommand
flashCmdList, err := shlex.Split(flashCmd) fileToken := "{" + fileExt[1:] + "}"
if err != nil { flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
return fmt.Errorf("could not parse flash command %#v: %w", flashCmd, err)
}
if strings.Contains(flashCmd, "{port}") { if strings.Contains(flashCmd, "{port}") {
var err error 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 { if err != nil {
return err return err
} }
} }
// Fill in fields in the command template. flashCmd = strings.ReplaceAll(flashCmd, "{port}", port)
fileToken := "{" + fileExt[1:] + "}"
for i, arg := range flashCmdList {
arg = strings.ReplaceAll(arg, fileToken, result.Binary)
arg = strings.ReplaceAll(arg, "{port}", port)
flashCmdList[i] = arg
}
// Execute the command. // Execute the command.
if len(flashCmdList) < 2 { var cmd *exec.Cmd
return fmt.Errorf("invalid flash command: %#v", flashCmd) 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.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Dir = goenv.Get("TINYGOROOT") cmd.Dir = goenv.Get("TINYGOROOT")
err = cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return &commandError{"failed to flash", result.Binary, err} return &commandError{"failed to flash", result.Binary, err}
} }
@@ -435,7 +381,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
case "native": case "native":
// Run GDB directly. // Run GDB directly.
case "openocd": 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. // We need a separate debugging daemon for on-chip debugging.
args, err := config.OpenOCDConfiguration() args, err := config.OpenOCDConfiguration()
@@ -454,7 +400,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
daemon.Stderr = w daemon.Stderr = w
} }
case "jlink": 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. // We need a separate debugging daemon for on-chip debugging.
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice) daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
@@ -469,7 +415,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
daemon.Stderr = w daemon.Stderr = w
} }
case "qemu": case "qemu":
gdbCommands = append(gdbCommands, "target extended-remote :1234") gdbCommands = append(gdbCommands, "target remote :1234")
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S") args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
@@ -477,7 +423,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "qemu-user": case "qemu-user":
gdbCommands = append(gdbCommands, "target extended-remote :1234") gdbCommands = append(gdbCommands, "target remote :1234")
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary) args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary)
@@ -485,7 +431,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "mgba": case "mgba":
gdbCommands = append(gdbCommands, "target extended-remote :2345") gdbCommands = append(gdbCommands, "target remote :2345")
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary, "-g") args := append(config.Target.Emulator[1:], result.Binary, "-g")
@@ -493,7 +439,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "simavr": case "simavr":
gdbCommands = append(gdbCommands, "target extended-remote :1234") gdbCommands = append(gdbCommands, "target remote :1234")
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], "-g", result.Binary) args := append(config.Target.Emulator[1:], "-g", result.Binary)
@@ -719,8 +665,7 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
} }
// getDefaultPort returns the default serial port depending on the operating system. // getDefaultPort returns the default serial port depending on the operating system.
func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err error) { func getDefaultPort(portCandidates []string) (port string, err error) {
portCandidates := strings.FieldsFunc(portFlag, func(c rune) bool { return c == ',' })
if len(portCandidates) == 1 { if len(portCandidates) == 1 {
return portCandidates[0], nil return portCandidates[0], nil
} }
@@ -736,70 +681,11 @@ func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err e
return "", err 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 { for _, p := range portsList {
if !p.IsUSB { ports = append(ports, p.Name)
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
} }
if len(ports) == 0 { if ports == nil || len(ports) == 0 {
// fallback // fallback
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin":
@@ -1009,7 +895,6 @@ func main() {
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)") gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)") panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks)") 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") printIR := flag.Bool("printir", false, "print LLVM IR")
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA") dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR") verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR")
@@ -1020,19 +905,16 @@ func main() {
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed") printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
printCommands := flag.Bool("x", false, "Print commands") printCommands := flag.Bool("x", false, "Print commands")
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation") nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug") 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)") port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
programmer := flag.String("programmer", "", "which hardware programmer to use") programmer := flag.String("programmer", "", "which hardware programmer to use")
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags") ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic") 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")
var flagJSON, flagDeps, flagTest *bool var flagJSON, flagDeps *bool
if command == "help" || command == "list" { if command == "help" || command == "list" {
flagJSON = flag.Bool("json", false, "print data in JSON format") flagJSON = flag.Bool("json", false, "print data in JSON format")
flagDeps = flag.Bool("deps", false, "supply -deps flag to go list") flagDeps = flag.Bool("deps", false, "")
flagTest = flag.Bool("test", false, "supply -test flag to go list")
} }
var outpath string var outpath string
if command == "help" || command == "build" || command == "build-library" || command == "test" { if command == "help" || command == "build" || command == "build-library" || command == "test" {
@@ -1061,7 +943,6 @@ func main() {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
var printAllocs *regexp.Regexp var printAllocs *regexp.Regexp
if *printAllocsString != "" { if *printAllocsString != "" {
printAllocs, err = regexp.Compile(*printAllocsString) printAllocs, err = regexp.Compile(*printAllocsString)
@@ -1070,35 +951,24 @@ func main() {
os.Exit(1) os.Exit(1)
} }
} }
var ocdCommands []string
if *ocdCommandsString != "" {
ocdCommands = strings.Split(*ocdCommandsString, ",")
}
options := &compileopts.Options{ options := &compileopts.Options{
Target: *target, Target: *target,
Opt: *opt, Opt: *opt,
GC: *gc, GC: *gc,
PanicStrategy: *panicStrategy, PanicStrategy: *panicStrategy,
Scheduler: *scheduler, Scheduler: *scheduler,
Serial: *serial, PrintIR: *printIR,
PrintIR: *printIR, DumpSSA: *dumpSSA,
DumpSSA: *dumpSSA, VerifyIR: *verifyIR,
VerifyIR: *verifyIR, Debug: !*nodebug,
Debug: !*nodebug, PrintSizes: *printSize,
PrintSizes: *printSize, PrintStacks: *printStacks,
PrintStacks: *printStacks, PrintAllocs: printAllocs,
PrintAllocs: printAllocs, PrintCommands: *printCommands,
Tags: *tags, Tags: *tags,
GlobalValues: globalVarValues, GlobalValues: globalVarValues,
WasmAbi: *wasmAbi, WasmAbi: *wasmAbi,
Programmer: *programmer, Programmer: *programmer,
OpenOCDCommands: ocdCommands,
LLVMFeatures: *llvmFeatures,
}
if *printCommands {
options.PrintCommands = printCommand
} }
os.Setenv("CC", "clang -target="+*target) os.Setenv("CC", "clang -target="+*target)
@@ -1191,26 +1061,16 @@ func main() {
err := Run(pkgName, options) err := Run(pkgName, options)
handleCompilerError(err) handleCompilerError(err)
case "test": case "test":
var pkgNames []string pkgName := "."
for i := 0; i < flag.NArg(); i++ { if flag.NArg() == 1 {
pkgNames = append(pkgNames, filepath.ToSlash(flag.Arg(i))) pkgName = filepath.ToSlash(flag.Arg(0))
} } else if flag.NArg() > 1 {
if len(pkgNames) == 0 { fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
pkgNames = []string{"."} usage()
}
allTestsPassed := true
for _, pkgName := range pkgNames {
// TODO: parallelize building the test binaries
passed, err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
handleCompilerError(err)
if !passed {
allTestsPassed = false
}
}
if !allTestsPassed {
fmt.Println("FAIL")
os.Exit(1) os.Exit(1)
} }
err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
handleCompilerError(err)
case "targets": case "targets":
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets") dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
entries, err := ioutil.ReadDir(dir) entries, err := ioutil.ReadDir(dir)
@@ -1285,9 +1145,6 @@ func main() {
if *flagDeps { if *flagDeps {
extraArgs = append(extraArgs, "-deps") extraArgs = append(extraArgs, "-deps")
} }
if *flagTest {
extraArgs = append(extraArgs, "-test")
}
cmd, err := loader.List(config, extraArgs, flag.Args()) cmd, err := loader.List(config, extraArgs, flag.Args())
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err) fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
+16 -15
View File
@@ -35,9 +35,9 @@ func TestCompiler(t *testing.T) {
"calls.go", "calls.go",
"cgo/", "cgo/",
"channel.go", "channel.go",
"coroutines.go",
"float.go", "float.go",
"gc.go", "gc.go",
"goroutines.go",
"init.go", "init.go",
"init_multi.go", "init_multi.go",
"interface.go", "interface.go",
@@ -125,7 +125,7 @@ func TestCompiler(t *testing.T) {
// Test with few optimizations enabled (no inlining, etc). // Test with few optimizations enabled (no inlining, etc).
t.Run("opt=1", func(t *testing.T) { t.Run("opt=1", func(t *testing.T) {
t.Parallel() t.Parallel()
runTestWithConfig("stdlib.go", "", t, compileopts.Options{ runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
Opt: "1", Opt: "1",
}, nil, nil) }, nil, nil)
}) })
@@ -134,14 +134,15 @@ func TestCompiler(t *testing.T) {
// TODO: fix this for stdlib.go, which currently fails. // TODO: fix this for stdlib.go, which currently fails.
t.Run("opt=0", func(t *testing.T) { t.Run("opt=0", func(t *testing.T) {
t.Parallel() t.Parallel()
runTestWithConfig("print.go", "", t, compileopts.Options{ runTestWithConfig("print.go", "", t, &compileopts.Options{
Opt: "0", Opt: "0",
}, nil, nil) }, nil, nil)
}) })
t.Run("ldflags", func(t *testing.T) { t.Run("ldflags", func(t *testing.T) {
t.Parallel() t.Parallel()
runTestWithConfig("ldflags.go", "", t, compileopts.Options{ runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
Opt: "z",
GlobalValues: map[string]map[string]string{ GlobalValues: map[string]map[string]string{
"main": { "main": {
"someGlobal": "foobar", "someGlobal": "foobar",
@@ -187,20 +188,20 @@ func runBuild(src, out string, opts *compileopts.Options) error {
} }
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) { func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
options := compileopts.Options{ options := &compileopts.Options{
Target: target, Target: target,
Opt: "z",
PrintIR: false,
DumpSSA: false,
VerifyIR: true,
Debug: true,
PrintSizes: "",
WasmAbi: "",
} }
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars) runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
} }
func runTestWithConfig(name, target string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) { 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"
}
// Get the expected output for this test. // Get the expected output for this test.
// Note: not using filepath.Join as it strips the path separator at the end // Note: not using filepath.Join as it strips the path separator at the end
// of the path. // of the path.
@@ -229,7 +230,7 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
// Build the test binary. // Build the test binary.
binary := filepath.Join(tmpdir, "test") binary := filepath.Join(tmpdir, "test")
err = runBuild("./"+path, binary, &options) err = runBuild("./"+path, binary, options)
if err != nil { if err != nil {
printCompilerError(t.Log, err) printCompilerError(t.Log, err)
t.Fail() t.Fail()
-19
View File
@@ -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)
}
-38
View File
@@ -1,38 +0,0 @@
// +build darwin freebsd wasi
// This implementation of crypto/rand uses the getentropy system call (available
// on both MacOS and WASI) to generate random numbers.
package rand
import (
"errors"
"unsafe"
)
var errReadFailed = errors.New("rand: could not read random bytes")
func init() {
Reader = &reader{}
}
type reader struct {
}
func (r *reader) Read(b []byte) (n int, err error) {
if len(b) != 0 {
if len(b) > 256 {
b = b[:256]
}
result := libc_getentropy(unsafe.Pointer(&b[0]), len(b))
if result < 0 {
// Maybe we should return a syscall.Errno here?
return 0, errReadFailed
}
}
return len(b), nil
}
// int getentropy(void *buf, size_t buflen);
//export getentropy
func libc_getentropy(buf unsafe.Pointer, buflen int) int
-38
View File
@@ -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)
}
-143
View File
@@ -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
View File
@@ -26,21 +26,12 @@ type SCB_Type struct {
SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2 SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2
SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3 SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3
// the following are only applicable for Cortex-M3/M33/M4/M7 // the following are only applicable for Cortex-M3/M33/M4/M7
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
HFSR volatile.Register32 // 0xD2C: HardFault Status Register HFSR volatile.Register32 // 0xD2C: HardFault Status Register
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
BFAR volatile.Register32 // 0xD38: BusFault 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
} }
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE))) var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
-15
View File
@@ -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()
}
-53
View File
@@ -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()
}
-75
View File
@@ -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)
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ import (
// change these to test a different UART or pins if available // change these to test a different UART or pins if available
var ( var (
uart = machine.Serial uart = machine.UART0
tx = machine.UART_TX_PIN tx = machine.UART_TX_PIN
rx = machine.UART_RX_PIN rx = machine.UART_RX_PIN
) )
-22
View File
@@ -1,22 +0,0 @@
package main
import (
"embed"
"log"
)
//go:embed file*.txt
//go:embed index.html styles.css
var files embed.FS
func main() {
println(".....")
println(msg)
contents, err := files.ReadDir(".")
if err != nil {
log.Fatal(err)
}
for _, c := range contents {
println("file:", c.Name())
}
}
-1
View File
@@ -1 +0,0 @@
file 1
-1
View File
@@ -1 +0,0 @@
file 2
-1
View File
@@ -1 +0,0 @@
file 3
View File
-1
View File
@@ -1 +0,0 @@
hello world!
View File
-6
View File
@@ -1,6 +0,0 @@
package main
import _ "embed"
//go:embed message.txt
var msg string
-32
View File
@@ -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)
}
}
-10
View File
@@ -1,10 +0,0 @@
// +build stm32
package main
import "machine"
const (
buttonMode = machine.PinInputPulldown
buttonPinChange = machine.PinRising | machine.PinFalling
)
-11
View File
@@ -1,11 +0,0 @@
// +build bluepill
package main
import "machine"
var (
pwm = &machine.TIM2
pinA = machine.PA0
pinB = machine.PA1
)
-11
View File
@@ -1,11 +0,0 @@
// +build stm32f7
package main
import "machine"
var (
pwm = &machine.TIM1
pinA = machine.PA8
pinB = machine.PA9
)
-11
View File
@@ -1,11 +0,0 @@
// +build stm32l0
package main
import "machine"
var (
pwm = &machine.TIM2
pinA = machine.PA0
pinB = machine.PB3
)
-11
View File
@@ -1,11 +0,0 @@
// +build stm32l4
package main
import "machine"
var (
pwm = &machine.TIM2
pinA = machine.PA0
pinB = machine.PB3
)
-11
View File
@@ -1,11 +0,0 @@
// +build stm32l5
package main
import "machine"
var (
pwm = &machine.TIM1
pinA = machine.PA8
pinB = machine.PA9
)
-13
View File
@@ -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
)
+1 -1
View File
@@ -13,7 +13,7 @@ type Task struct {
Ptr unsafe.Pointer Ptr unsafe.Pointer
// Data is a field which can be used for storing state information. // 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 is the underlying running state of the task.
state state state state
-58
View File
@@ -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
-59
View File
@@ -1,59 +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
}
// 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
}
-74
View File
@@ -1,74 +0,0 @@
#ifdef __MACH__ // Darwin
.global _tinygo_startTask
_tinygo_startTask:
#else // Linux etc
.section .text.tinygo_startTask
.global tinygo_startTask
tinygo_startTask:
#endif
.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, r12 contain the pc of the to-be-started function and
// r13 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 like here:
// #10 0x00000000004277b6 in <goroutine wrapper> () at [...]
// #11 0x00000000004278f3 in tinygo_startTask () at [...]
// #12 0x0000000000002030 in ?? ()
// #13 0x0000000000000071 in ?? ()
.cfi_undefined rip
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
movq %r13, %rdi
// Branch to the "goroutine start" function.
callq *%r12
// After return, exit this goroutine. This is a tail call.
#ifdef __MACH__
jmp _tinygo_pause
#else
jmp tinygo_pause
#endif
.cfi_endproc
#ifdef __MACH__ // Darwin
.global _tinygo_swapTask
_tinygo_swapTask:
#else // Linux etc
.global tinygo_swapTask
.section .text.tinygo_swapTask
tinygo_swapTask:
#endif
// This function gets the following parameters:
// %rdi = newStack uintptr
// %rsi = oldStack *uintptr
// Save all callee-saved registers:
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbp
pushq %rbx
// Save the current stack pointer in oldStack.
movq %rsp, (%rsi)
// Switch to the new stack pointer.
movq %rdi, %rsp
// Load saved register from the new stack.
popq %rbx
popq %rbp
popq %r12
popq %r13
popq %r14
popq %r15
// Return into the new task, as if tinygo_swapTask was a regular call.
ret
-61
View File
@@ -1,61 +0,0 @@
// +build scheduler.tasks,amd64
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_amd64.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
rbx uintptr
rbp uintptr
r12 uintptr
r13 uintptr
r14 uintptr
r15 uintptr
pc 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_amd64.S). This assembly code calls a
// function (passed in r12) with a single argument (passed in r13). After
// the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in r12.
// 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.r12 = fn
// Pass the pointer to the arguments struct in r13.
r.r13 = 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
}
-51
View File
@@ -1,51 +0,0 @@
// Only generate .debug_frame, don't generate .eh_frame.
.cfi_sections .debug_frame
.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, r4 contains the pc of the to-be-started function and r5
// contains the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Indicate to the unwinder that there is nothing to unwind, this is the
// root frame. It avoids the following (bogus) error message in GDB:
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
.cfi_undefined lr
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
mov r0, r5
// Branch to the "goroutine start" function. By using blx instead of bx,
// we'll return here instead of tail calling.
blx r4
// After return, exit this goroutine. This is a tail call.
bl tinygo_pause
.cfi_endproc
.size tinygo_startTask, .-tinygo_startTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// r0 = newStack uintptr
// r1 = oldStack *uintptr
// Save all callee-saved registers:
push {r4-r11, lr}
// Save the current stack pointer in oldStack.
str sp, [r1]
// Switch to the new stack pointer.
mov sp, r0
// Load state from new task and branch to the previous position in the
// program.
pop {r4-r11, pc}
-61
View File
@@ -1,61 +0,0 @@
// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa
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_arm.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
r4 uintptr
r5 uintptr
r6 uintptr
r7 uintptr
r8 uintptr
r9 uintptr
r10 uintptr
r11 uintptr
pc 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_arm.S).
// This assembly code calls a function (passed in r4) with a single argument
// (passed in r5). After the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in r4.
// 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.r4 = fn
// Pass the pointer to the arguments struct in r5.
r.r5 = 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
}
-59
View File
@@ -1,59 +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, x19 contains the pc of the to-be-started function and
// x20 contains 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 the following (bogus) error message in GDB:
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
.cfi_undefined lr
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
mov x0, x20
// Branch to the "goroutine start" function. By using blx instead of bx,
// we'll return here instead of tail calling.
blr x19
// After return, exit this goroutine. This is a tail call.
b tinygo_pause
.cfi_endproc
.size tinygo_startTask, .-tinygo_startTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// x0 = newStack uintptr
// x1 = oldStack *uintptr
// Save all callee-saved registers:
stp x19, x20, [sp, #-96]!
stp x21, x22, [sp, #16]
stp x23, x24, [sp, #32]
stp x25, x26, [sp, #48]
stp x27, x28, [sp, #64]
stp x29, x30, [sp, #80]
// Save the current stack pointer in oldStack.
mov x8, sp
str x8, [x1]
// Switch to the new stack pointer.
mov sp, x0
// Restore stack state and return.
ldp x29, x30, [sp, #80]
ldp x27, x28, [sp, #64]
ldp x25, x26, [sp, #48]
ldp x23, x24, [sp, #32]
ldp x21, x22, [sp, #16]
ldp x19, x20, [sp], #96
ret
-64
View File
@@ -1,64 +0,0 @@
// +build scheduler.tasks,arm64
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_arm64.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
x19 uintptr
x20 uintptr
x21 uintptr
x22 uintptr
x23 uintptr
x24 uintptr
x25 uintptr
x26 uintptr
x27 uintptr
x28 uintptr
x29 uintptr
pc uintptr // aka x30 aka LR
}
// 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_arm64.S).
// This assembly code calls a function (passed in x19) with a single argument
// (passed in x20). After the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in x19.
// 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.x19 = fn
// Pass the pointer to the arguments struct in x20.
r.x20 = 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
}
-6
View File
@@ -2,12 +2,6 @@
package task package task
// Note that this is almost the same as task_stack_arm.go, but it uses the MSP
// register to store the system stack pointer instead of a global variable. The
// big advantage of this is that interrupts always execute with MSP (and not
// PSP, which is used for goroutines) so that goroutines do not need extra stack
// space for interrupts.
import ( import (
"device/arm" "device/arm"
"unsafe" "unsafe"
+1 -1
View File
@@ -47,7 +47,7 @@ const (
LED = D6 LED = D6
) )
// USBCDC pins // UART0 aka USBCDC pins
const ( const (
USBCDC_DM_PIN Pin = PA24 USBCDC_DM_PIN Pin = PA24
USBCDC_DP_PIN Pin = PA25 USBCDC_DP_PIN Pin = PA25
+1 -1
View File
@@ -46,7 +46,7 @@ const (
LED = D13 LED = D13
) )
// USBCDC pins // UART0 aka USBCDC pins
const ( const (
USBCDC_DM_PIN Pin = PA24 USBCDC_DM_PIN Pin = PA24
USBCDC_DP_PIN Pin = PA25 USBCDC_DP_PIN Pin = PA25
@@ -9,8 +9,7 @@ import (
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip. // UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
var ( var (
UART1 = &_UART1 UART1 = UART{
_UART1 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: sam.SERCOM3_USART, Bus: sam.SERCOM3_USART,
SERCOM: 3, SERCOM: 3,
@@ -19,8 +18,7 @@ var (
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins. // UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
var ( var (
UART2 = &_UART2 UART2 = UART{
_UART2 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART, Bus: sam.SERCOM5_USART,
SERCOM: 5, SERCOM: 5,
@@ -28,8 +26,8 @@ var (
) )
func init() { func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3, _UART1.handleInterrupt) UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5, _UART2.handleInterrupt) UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5, UART2.handleInterrupt)
} }
// I2C on the Arduino Nano 33. // I2C on the Arduino Nano 33.
+9 -24
View File
@@ -152,7 +152,7 @@ const (
PIN_USB_ID = PC19 PIN_USB_ID = PC19
) )
// USBCDC pins // UART0 aka USBCDC pins
const ( const (
USBCDC_DM_PIN = PA24 USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25 USBCDC_DP_PIN = PA25
@@ -240,32 +240,28 @@ var (
// UART on the SAM E54 Xplained Pro // UART on the SAM E54 Xplained Pro
var ( var (
// Extension Header EXT1 // Extension Header EXT1
UART1 = &_UART1 UART1 = UART{
_UART1 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT, Bus: sam.SERCOM0_USART_INT,
SERCOM: 0, SERCOM: 0,
} }
// Extension Header EXT2 // Extension Header EXT2
UART2 = &_UART2 UART2 = UART{
_UART2 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT, Bus: sam.SERCOM5_USART_INT,
SERCOM: 5, SERCOM: 5,
} }
// Extension Header EXT3 // Extension Header EXT3
UART3 = &_UART3 UART3 = UART{
_UART3 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT, Bus: sam.SERCOM1_USART_INT,
SERCOM: 1, SERCOM: 1,
} }
// EDBG Virtual COM Port // EDBG Virtual COM Port
UART4 = &_UART4 UART4 = UART{
_UART4 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: sam.SERCOM2_USART_INT, Bus: sam.SERCOM2_USART_INT,
SERCOM: 2, SERCOM: 2,
@@ -273,10 +269,10 @@ var (
) )
func init() { func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, _UART1.handleInterrupt) UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, _UART2.handleInterrupt) UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, _UART3.handleInterrupt) UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART3.handleInterrupt)
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, _UART4.handleInterrupt) UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, UART4.handleInterrupt)
} }
// I2C on the SAM E54 Xplained Pro // I2C on the SAM E54 Xplained Pro
@@ -332,14 +328,3 @@ var (
SERCOM: 6, SERCOM: 6,
} }
) )
// CAN on the SAM E54 Xplained Pro
var (
CAN0 = CAN{
Bus: sam.CAN0,
}
CAN1 = CAN{
Bus: sam.CAN1,
}
)
+44 -14
View File
@@ -7,18 +7,49 @@ import (
"runtime/interrupt" "runtime/interrupt"
) )
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PA8 = portA + 8
PA9 = portA + 9
PA10 = portA + 10
PA11 = portA + 11
PA12 = portA + 12
PA13 = portA + 13
PA14 = portA + 14
PA15 = portA + 15
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PB8 = portB + 8
PB9 = portB + 9
PB10 = portB + 10
PB11 = portB + 11
PB12 = portB + 12
PB13 = portB + 13
PB14 = portB + 14
PB15 = portB + 15
PC13 = portC + 13
PC14 = portC + 14
PC15 = portC + 15
)
const ( const (
LED = PC13 LED = PC13
) )
const (
// This board does not have a user button, so
// use first GPIO pin by default
BUTTON = PA0
)
var DefaultUART = UART1
// UART pins // UART pins
const ( const (
UART_TX_PIN = PA9 UART_TX_PIN = PA9
@@ -29,21 +60,20 @@ const (
var ( var (
// USART1 is the first hardware serial port on the STM32. // USART1 is the first hardware serial port on the STM32.
UART1 = &_UART1 // Both UART0 and UART1 refer to USART1.
_UART1 = UART{ UART0 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: stm32.USART1, Bus: stm32.USART1,
} }
UART2 = &_UART2 UART1 = UART{
_UART2 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: stm32.USART2, Bus: stm32.USART2,
} }
) )
func init() { func init() {
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART1.handleInterrupt) UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
UART2.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART2.handleInterrupt) UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, UART1.handleInterrupt)
} }
// SPI pins // SPI pins
+5 -1
View File
@@ -38,7 +38,6 @@ const (
const ( const (
LED = D13 LED = D13
NEOPIXELS = D8 NEOPIXELS = D8
WS2812 = D8
BUTTONA = D4 BUTTONA = D4
BUTTONB = D5 BUTTONB = D5
@@ -57,6 +56,11 @@ const (
UART_RX_PIN = P0_30 // PORTB UART_RX_PIN = P0_30 // PORTB
) )
// UART0 is the USB device
var (
UART0 = &USB
)
// I2C pins // I2C pins
const ( const (
SDA_PIN = P0_05 // I2C0 external SDA_PIN = P0_05 // I2C0 external
-1
View File
@@ -41,7 +41,6 @@ const (
const ( const (
LED = D13 LED = D13
NEOPIXELS = D8 NEOPIXELS = D8
WS2812 = D8
BUTTONA = D4 BUTTONA = D4
BUTTONB = D5 BUTTONB = D5

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