mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-28 15:48:40 +00:00
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3ee85890d | |||
| 2930c44bfc | |||
| 0565b7c0e0 | |||
| 444dded92c | |||
| 42785e08e8 | |||
| 2d633e3a28 | |||
| 64d048c47c | |||
| c8e231bc0b | |||
| b00cfc001e | |||
| e127ceac67 | |||
| e5453ebe27 | |||
| 0e267dd230 | |||
| 96e863f0f3 | |||
| 75298bb84b | |||
| e65592599c | |||
| e02f308d43 | |||
| bfe3f68647 | |||
| 2bb70812a8 | |||
| 293f4ea7bc | |||
| c3032660c9 | |||
| d94f42f6e2 | |||
| d8ac7ccaae | |||
| 64058c3efb | |||
| e107efa63f | |||
| 8e33f1c9eb | |||
| 1913cb76a5 | |||
| cd628bcde6 | |||
| d1f445735c | |||
| 28a2ad4757 | |||
| f2e8d7112c | |||
| 52d640967b | |||
| 87e48c1057 | |||
| b406b81416 | |||
| 3a458ec75c | |||
| 7764797061 | |||
| 5f9e339cf3 | |||
| c017ed2242 | |||
| d62a9e24e5 | |||
| b092856238 | |||
| 15d77119c9 | |||
| 95af444896 | |||
| 1f5e4e79aa | |||
| 9912dd6db1 | |||
| 42ec3e2469 | |||
| 4e610a0ee7 | |||
| af65c006e6 | |||
| 98f117fca4 | |||
| 4c95febeee | |||
| 793a3175d3 | |||
| 78d030aa7a | |||
| 62f9f61664 | |||
| 5b82125765 | |||
| 2f248bbf8b | |||
| c5ea1fde61 | |||
| 36dffb5554 | |||
| e8c4c4a865 | |||
| 8b79e82686 | |||
| 22eeed2da1 | |||
| cfbc9be9bf | |||
| 722a3a5c94 | |||
| ed2db8a26d | |||
| fd8938c634 | |||
| fa3dd41a4f | |||
| 003c96edc0 | |||
| ac54302301 | |||
| c43a41165a | |||
| 3145c2747e | |||
| 2c4b507d34 | |||
| ee167f15de | |||
| 422ebeeec7 | |||
| c93ddb630b | |||
| 3edcdb5f0d | |||
| ec325c0643 | |||
| 87c2ccb0b9 | |||
| 6e1eb28ed0 | |||
| 45cf2a5a1a | |||
| 423cae86df | |||
| ea6b65e360 | |||
| e3b98dabfd | |||
| e312cb0fe7 | |||
| 541d8dcd2e | |||
| c8742e2b96 | |||
| 711889bc3f | |||
| 70f8eeaca0 | |||
| 3339d0f47e | |||
| a38b5c4e01 | |||
| b20905da13 | |||
| 95e4dcfb53 | |||
| 841f19f49e | |||
| 2085ffb334 | |||
| b67351babe | |||
| aa5b8d0df7 | |||
| 7c949ad386 | |||
| 1646326164 | |||
| e44cb7e519 | |||
| d1e96fd0a8 | |||
| ff5d0c9886 | |||
| d7ec9ddcd2 | |||
| e7c6bd3730 | |||
| 9f5066aa6f | |||
| 3b24fedf92 | |||
| 8cd2a462b9 | |||
| 25b045d0a7 | |||
| 78acbdf0d9 | |||
| 617e2791ef | |||
| a0908ff55b | |||
| 2f1f8fb075 | |||
| 6f61b83ad5 | |||
| 8dfefb46d1 | |||
| 959442dc82 | |||
| c1aa152a63 | |||
| dd3d8a363a | |||
| bb509ec91d | |||
| 944f022060 | |||
| cd517a30af | |||
| 52d8655eec | |||
| f5786941e5 | |||
| 1f73941c43 | |||
| abeab51d00 | |||
| 9ef75f17bf | |||
| c3992bd77b | |||
| f79e66ac2e | |||
| 4eac212695 |
+14
-14
@@ -157,7 +157,7 @@ commands:
|
|||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-11-linux-v3-assert
|
- llvm-build-11-linux-v4-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-v3-assert
|
key: llvm-build-11-linux-v4-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-v3-noassert
|
- llvm-build-11-linux-v4-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-v3-noassert
|
key: llvm-build-11-linux-v4-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-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
- go-cache-macos-v3-{{ checksum "go.mod" }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-11-macos-v2
|
- llvm-source-11-macos-v3
|
||||||
- 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-v2
|
key: llvm-source-11-macos-v3
|
||||||
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-v3
|
- llvm-build-11-macos-v5
|
||||||
- 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-v3
|
key: llvm-build-11-macos-v5
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-macos-v3
|
- wasi-libc-sysroot-macos-v4
|
||||||
- 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-v3
|
key: wasi-libc-sysroot-macos-v4
|
||||||
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-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-macos-v3-{{ 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: "10.1.0"
|
xcode: "11.1.0" # macOS 10.14
|
||||||
steps:
|
steps:
|
||||||
- build-macos
|
- build-macos
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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 }}
|
||||||
@@ -16,6 +16,8 @@ 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
@@ -1,3 +1,191 @@
|
|||||||
|
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
@@ -1,5 +1,5 @@
|
|||||||
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 11 and the TinyGo compiler itself.
|
# TinyGo base stage installs the most recent Go 1.16.x, LLVM 11 and the TinyGo compiler itself.
|
||||||
FROM golang:1.15 AS tinygo-base
|
FROM golang:1.16 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 && \
|
||||||
|
|||||||
@@ -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
|
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||||
|
|
||||||
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 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_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_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: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp
|
||||||
ifneq ($(STM32), 0)
|
ifneq ($(STM32), 0)
|
||||||
gen-device: gen-device-stm32
|
gen-device: gen-device-stm32
|
||||||
endif
|
endif
|
||||||
@@ -150,6 +150,9 @@ 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:
|
||||||
@@ -159,7 +162,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 $(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 -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||||
@@ -182,25 +185,28 @@ 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: do this in one command, parallelize, and only show failing tests (no
|
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||||
# implied -v flag).
|
|
||||||
.PHONY: tinygo-test
|
.PHONY: tinygo-test
|
||||||
tinygo-test:
|
tinygo-test:
|
||||||
$(TINYGO) test container/heap
|
$(TINYGO) test $(TEST_PACKAGES)
|
||||||
$(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:
|
||||||
@@ -224,6 +230,8 @@ 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
|
||||||
@@ -328,9 +336,11 @@ 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/blinky1
|
$(TINYGO) build -size short -o test.hex -target=qtpy examples/serial
|
||||||
@$(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
|
||||||
@@ -340,8 +350,22 @@ 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
|
||||||
@@ -372,6 +396,8 @@ 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
|
||||||
@@ -412,6 +438,8 @@ 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
|
||||||
|
|||||||
@@ -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 57 microcontroller boards are currently supported:
|
The following 68 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,7 +52,10 @@ The following 57 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)
|
||||||
@@ -63,10 +66,14 @@ The following 57 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 Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
||||||
|
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
||||||
|
* [Arduino Nano 33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||||
|
* [Arduino Nano RP2040 Connect](https://store.arduino.cc/nano-rp2040-connect)
|
||||||
* [Arduino 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/)
|
||||||
@@ -84,6 +91,7 @@ The following 57 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/)
|
||||||
@@ -92,12 +100,15 @@ The following 57 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
@@ -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-v4
|
key: llvm-build-11-windows-v5
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- task: Bash@3
|
- task: Bash@3
|
||||||
displayName: Build LLVM
|
displayName: Build LLVM
|
||||||
|
|||||||
+128
-43
@@ -12,7 +12,9 @@ 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"
|
||||||
@@ -39,6 +41,11 @@ 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
|
||||||
@@ -93,7 +100,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
DefaultStackSize: config.Target.DefaultStackSize,
|
DefaultStackSize: config.Target.DefaultStackSize,
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
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
|
||||||
@@ -529,6 +536,41 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip debug information if -debug is true. This is sometimes the default,
|
||||||
|
// such as with WebAssembly targets.
|
||||||
|
if !config.Debug() {
|
||||||
|
for _, tag := range config.BuildTags() {
|
||||||
|
if tag == "baremetal" {
|
||||||
|
// Don't use -debug=false on baremetal targets. It makes no
|
||||||
|
// sense: the debug information isn't flashed to the device
|
||||||
|
// anyway.
|
||||||
|
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.Target.Linker == "wasm-ld" {
|
||||||
|
// Don't just strip debug information, also compress relocations
|
||||||
|
// while we're at it. Relocations can only be compressed when debug
|
||||||
|
// information is stripped.
|
||||||
|
ldflags = append(ldflags, "--strip-debug", "--compress-relocations")
|
||||||
|
} else {
|
||||||
|
switch config.GOOS() {
|
||||||
|
case "linux":
|
||||||
|
// Either real linux or an embedded system (like AVR) that
|
||||||
|
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||||
|
// case.
|
||||||
|
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||||
|
case "darwin":
|
||||||
|
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||||
|
// information. Apple expects you to use the strip command
|
||||||
|
// instead.
|
||||||
|
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||||
|
default:
|
||||||
|
// Other OSes may have different flags.
|
||||||
|
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a linker job, which links all object files together and does some
|
// 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{
|
||||||
@@ -541,8 +583,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 {
|
if config.Options.PrintCommands != nil {
|
||||||
fmt.Printf("%s %s\n", config.Target.Linker, strings.Join(ldflags, " "))
|
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||||
}
|
}
|
||||||
err = link(config.Target.Linker, ldflags...)
|
err = link(config.Target.Linker, ldflags...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -560,6 +602,8 @@ 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.
|
||||||
@@ -568,6 +612,13 @@ 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)
|
||||||
@@ -633,12 +684,25 @@ 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,30 +965,7 @@ 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 {
|
||||||
fp, err := os.OpenFile(executable, os.O_RDWR, 0)
|
data, fileHeader, err := getElfSectionData(executable, ".tinygo_stacksizes")
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -946,15 +987,19 @@ 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)
|
||||||
|
|
||||||
// Adding 4 for the stack canary. Even though the size may be
|
|
||||||
// automatically determined, stack overflow checking is still
|
|
||||||
// important as the stack size cannot be determined for all
|
|
||||||
// goroutines.
|
|
||||||
stackSize += 4
|
|
||||||
|
|
||||||
// Add stack size used by interrupts.
|
// Add stack size used by interrupts.
|
||||||
switch elfFile.Machine {
|
switch fileHeader.Machine {
|
||||||
case elf.EM_ARM:
|
case elf.EM_ARM:
|
||||||
|
if stackSize%8 != 0 {
|
||||||
|
// If the stack isn't a multiple of 8, it means the leaf
|
||||||
|
// function with the biggest stack depth doesn't have an aligned
|
||||||
|
// stack. If the STKALIGN flag is set (which it is by default)
|
||||||
|
// the interrupt controller will forcibly align the stack before
|
||||||
|
// storing in-use registers. This will thus overwrite one word
|
||||||
|
// past the end of the stack (off-by-one).
|
||||||
|
stackSize += 4
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -962,6 +1007,14 @@ 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.
|
||||||
@@ -969,13 +1022,7 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write back the modified stack sizes.
|
return replaceElfSection(executable, ".tinygo_stacksizes", data)
|
||||||
_, 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
|
||||||
@@ -1007,3 +1054,41 @@ 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
@@ -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 bool) (string, error) {
|
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (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 {
|
if printCommands != nil {
|
||||||
fmt.Printf("clang %s\n", strings.Join(flags, " "))
|
printCommands("clang", flags...)
|
||||||
}
|
}
|
||||||
err = runCCompiler(flags...)
|
err = runCCompiler(flags...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -17,10 +17,18 @@ 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)
|
||||||
@@ -28,7 +36,9 @@ 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,
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
+164
-74
@@ -11,105 +11,184 @@ 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)
|
expr, err := parseConstExpr(t, precedenceLowest)
|
||||||
if t.token != token.EOF {
|
t.Next()
|
||||||
|
if t.curToken != token.EOF {
|
||||||
return nil, &scanner.Error{
|
return nil, &scanner.Error{
|
||||||
Pos: t.fset.Position(t.pos),
|
Pos: t.fset.Position(t.curPos),
|
||||||
Msg: "unexpected token " + t.token.String(),
|
Msg: "unexpected token " + t.curToken.String() + ", expected end of expression",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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) (ast.Expr, *scanner.Error) {
|
func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
|
||||||
switch t.token {
|
if t.curToken == token.EOF {
|
||||||
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.pos),
|
Pos: t.fset.Position(t.curPos),
|
||||||
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.pos),
|
Pos: t.fset.Position(t.curPos),
|
||||||
Msg: fmt.Sprintf("unexpected token %s", t.token),
|
Msg: fmt.Sprintf("unexpected token %s", t.curToken),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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.pos),
|
Pos: t.fset.Position(t.curPos),
|
||||||
Msg: fmt.Sprintf("unexpected token %s, expected %s", t.token, expected),
|
Msg: fmt.Sprintf("unexpected token %s, expected %s", t.curToken, 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 {
|
||||||
pos token.Pos
|
curPos, peekPos token.Pos
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
token token.Token
|
curToken, peekToken token.Token
|
||||||
value string
|
curValue, peekValue 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{
|
||||||
pos: start,
|
peekPos: start,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
buf: buf,
|
buf: buf,
|
||||||
token: token.ILLEGAL,
|
peekToken: token.ILLEGAL,
|
||||||
}
|
}
|
||||||
t.Next() // Parse the first token.
|
// Parse the first two tokens (cur and peek).
|
||||||
|
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() {
|
||||||
t.pos += token.Pos(len(t.value))
|
// The previous peek is now the current token.
|
||||||
|
t.curPos = t.peekPos
|
||||||
|
t.curToken = t.peekToken
|
||||||
|
t.curValue = t.peekValue
|
||||||
|
|
||||||
|
// Parse the next peek token.
|
||||||
|
t.peekPos += token.Pos(len(t.curValue))
|
||||||
for {
|
for {
|
||||||
if len(t.buf) == 0 {
|
if len(t.buf) == 0 {
|
||||||
t.token = token.EOF
|
t.peekToken = token.EOF
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c := t.buf[0]
|
c := t.buf[0]
|
||||||
@@ -118,17 +197,28 @@ 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.pos++
|
t.peekPos++
|
||||||
t.buf = t.buf[1:]
|
t.buf = t.buf[1:]
|
||||||
case c == '(' || c == ')':
|
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%':
|
||||||
// Single-character tokens.
|
// Single-character tokens.
|
||||||
|
// TODO: ++ (increment) and -- (decrement) operators.
|
||||||
switch c {
|
switch c {
|
||||||
case '(':
|
case '(':
|
||||||
t.token = token.LPAREN
|
t.peekToken = token.LPAREN
|
||||||
case ')':
|
case ')':
|
||||||
t.token = token.RPAREN
|
t.peekToken = token.RPAREN
|
||||||
|
case '+':
|
||||||
|
t.peekToken = token.ADD
|
||||||
|
case '-':
|
||||||
|
t.peekToken = token.SUB
|
||||||
|
case '*':
|
||||||
|
t.peekToken = token.MUL
|
||||||
|
case '/':
|
||||||
|
t.peekToken = token.QUO
|
||||||
|
case '%':
|
||||||
|
t.peekToken = token.REM
|
||||||
}
|
}
|
||||||
t.value = t.buf[:1]
|
t.peekValue = 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':
|
||||||
@@ -146,17 +236,17 @@ func (t *tokenizer) Next() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.value = t.buf[:tokenLen]
|
t.peekValue = 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.token = token.FLOAT
|
t.peekToken = token.FLOAT
|
||||||
t.value = strings.TrimRight(t.value, "f")
|
t.peekValue = strings.TrimRight(t.peekValue, "f")
|
||||||
} else {
|
} else {
|
||||||
t.token = token.INT
|
t.peekToken = token.INT
|
||||||
t.value = strings.TrimRight(t.value, "uUlL")
|
t.peekValue = strings.TrimRight(t.peekValue, "uUlL")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_':
|
case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_':
|
||||||
@@ -170,9 +260,9 @@ func (t *tokenizer) Next() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.value = t.buf[:tokenLen]
|
t.peekValue = t.buf[:tokenLen]
|
||||||
t.buf = t.buf[tokenLen:]
|
t.buf = t.buf[tokenLen:]
|
||||||
t.token = token.IDENT
|
t.peekToken = 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
|
||||||
@@ -188,8 +278,8 @@ func (t *tokenizer) Next() {
|
|||||||
escape = c == '\\'
|
escape = c == '\\'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.token = token.STRING
|
t.peekToken = token.STRING
|
||||||
t.value = t.buf[:tokenLen]
|
t.peekValue = t.buf[:tokenLen]
|
||||||
t.buf = t.buf[tokenLen:]
|
t.buf = t.buf[tokenLen:]
|
||||||
return
|
return
|
||||||
case c == '\'':
|
case c == '\'':
|
||||||
@@ -206,12 +296,12 @@ func (t *tokenizer) Next() {
|
|||||||
escape = c == '\\'
|
escape = c == '\\'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.token = token.CHAR
|
t.peekToken = token.CHAR
|
||||||
t.value = t.buf[:tokenLen]
|
t.peekValue = t.buf[:tokenLen]
|
||||||
t.buf = t.buf[tokenLen:]
|
t.buf = t.buf[tokenLen:]
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
t.token = token.ILLEGAL
|
t.peekToken = token.ILLEGAL
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-2
@@ -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 )`},
|
{`5)`, `error: 1:2: unexpected token ), expected end of expression`},
|
||||||
{" \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,7 +30,24 @@ 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`}, // test for a bugfix
|
{`5 5`, `error: 1:3: unexpected token INT, expected end of expression`}, // test for a bugfix
|
||||||
|
// Binary operators.
|
||||||
|
{`5+5`, `5 + 5`},
|
||||||
|
{`5-5`, `5 - 5`},
|
||||||
|
{`5*5`, `5 * 5`},
|
||||||
|
{`5/5`, `5 / 5`},
|
||||||
|
{`5%5`, `5 % 5`},
|
||||||
|
{`(5/5)`, `(5 / 5)`},
|
||||||
|
{`1 - 2`, `1 - 2`},
|
||||||
|
{`1 - 2 + 3`, `1 - 2 + 3`},
|
||||||
|
{`1 - 2 * 3`, `1 - 2*3`},
|
||||||
|
{`(1 - 2) * 3`, `(1 - 2) * 3`},
|
||||||
|
{`1 * 2 - 3`, `1*2 - 3`},
|
||||||
|
{`1 * (2 - 3)`, `1 * (2 - 3)`},
|
||||||
|
// Unary operators.
|
||||||
|
{`-5`, `-5`},
|
||||||
|
{`-5-2`, `-5 - 2`},
|
||||||
|
{`5 - - 2`, `5 - -2`},
|
||||||
} {
|
} {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -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 )
|
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
||||||
|
|
||||||
// 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)
|
||||||
|
|||||||
+55
-14
@@ -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()}...)
|
tags := append(c.Target.BuildTags, []string{"tinygo", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||||
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,12 +80,7 @@ func (c *Config) GC() string {
|
|||||||
if c.Target.GC != "" {
|
if c.Target.GC != "" {
|
||||||
return c.Target.GC
|
return c.Target.GC
|
||||||
}
|
}
|
||||||
for _, tag := range c.Target.BuildTags {
|
return "conservative"
|
||||||
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
|
||||||
@@ -94,7 +89,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 == "wasm" {
|
if tag == "tinygo.wasm" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,6 +113,18 @@ 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) {
|
||||||
@@ -181,6 +188,15 @@ 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 {
|
||||||
@@ -193,9 +209,8 @@ func (c *Config) CFlags() []string {
|
|||||||
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
||||||
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
||||||
}
|
}
|
||||||
if c.Debug() {
|
// Always emit debug information. It is optionally stripped at link time.
|
||||||
cflags = append(cflags, "-g")
|
cflags = append(cflags, "-g")
|
||||||
}
|
|
||||||
return cflags
|
return cflags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,10 +249,27 @@ func (c *Config) VerifyIR() bool {
|
|||||||
return c.Options.VerifyIR
|
return c.Options.VerifyIR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug returns whether to add debug symbols to the IR, for debugging with GDB
|
// Debug returns whether debug (DWARF) information should be retained by the
|
||||||
// and similar.
|
// linker. The default varies by target but can be controlled with the -debug
|
||||||
|
// command line flag.
|
||||||
func (c *Config) Debug() bool {
|
func (c *Config) Debug() bool {
|
||||||
return c.Options.Debug
|
switch c.Options.Debug {
|
||||||
|
case "true":
|
||||||
|
return true
|
||||||
|
case "false":
|
||||||
|
return false
|
||||||
|
case "auto":
|
||||||
|
// Emit debug information everywhere by default except on WebAssembly.
|
||||||
|
for _, tag := range c.BuildTags() {
|
||||||
|
if tag == "tinygo.wasm" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
// This is already checked so shouldn't happen.
|
||||||
|
panic("unknown -debug flag")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BinaryFormat returns an appropriate binary format, based on the file
|
// BinaryFormat returns an appropriate binary format, based on the file
|
||||||
@@ -259,6 +291,11 @@ 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"
|
||||||
@@ -342,6 +379,10 @@ 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
|
||||||
|
|||||||
+38
-18
@@ -9,6 +9,8 @@ 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"}
|
||||||
|
validDebugOptions = []string{"auto", "true", "false"}
|
||||||
|
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"}
|
||||||
@@ -17,24 +19,27 @@ 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
|
||||||
PrintIR bool
|
Serial string
|
||||||
DumpSSA bool
|
PrintIR bool
|
||||||
VerifyIR bool
|
DumpSSA bool
|
||||||
PrintCommands bool
|
VerifyIR bool
|
||||||
Debug bool
|
PrintCommands func(cmd string, args ...string)
|
||||||
PrintSizes string
|
Debug string
|
||||||
PrintAllocs *regexp.Regexp // regexp string
|
PrintSizes string
|
||||||
PrintStacks bool
|
PrintAllocs *regexp.Regexp // regexp string
|
||||||
Tags string
|
PrintStacks bool
|
||||||
WasmAbi string
|
Tags string
|
||||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
WasmAbi string
|
||||||
TestConfig TestConfig
|
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||||
Programmer string
|
TestConfig TestConfig
|
||||||
|
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.
|
||||||
@@ -57,6 +62,21 @@ 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 !isInArray(validDebugOptions, o.Debug) {
|
||||||
|
return fmt.Errorf(`invalid debug option '%s': valid values are %s`,
|
||||||
|
o.Debug,
|
||||||
|
strings.Join(validDebugOptions, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
if o.PrintSizes != "" {
|
if o.PrintSizes != "" {
|
||||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
||||||
if !valid {
|
if !valid {
|
||||||
|
|||||||
+15
-8
@@ -31,6 +31,7 @@ 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"`
|
||||||
@@ -40,10 +41,12 @@ 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"`
|
||||||
@@ -239,22 +242,26 @@ 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},
|
||||||
Linker: "cc",
|
Scheduler: "tasks",
|
||||||
CFlags: []string{"--target=" + triple},
|
Linker: "cc",
|
||||||
GDB: []string{"gdb"},
|
DefaultStackSize: 1024 * 64, // 64kB
|
||||||
PortReset: "false",
|
CFlags: []string{"--target=" + triple},
|
||||||
|
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.
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ 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
|
||||||
|
|||||||
+107
-101
@@ -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 = 9 // last change: implement reflect.New()
|
const Version = 12 // last change: implement syscall.rawSyscallNoError
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
@@ -58,7 +58,7 @@ type Config struct {
|
|||||||
AutomaticStackSize bool
|
AutomaticStackSize bool
|
||||||
DefaultStackSize uint64
|
DefaultStackSize uint64
|
||||||
NeedsStackObjects bool
|
NeedsStackObjects bool
|
||||||
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
|
||||||
@@ -102,9 +102,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
|||||||
c.mod = c.ctx.NewModule(moduleName)
|
c.mod = c.ctx.NewModule(moduleName)
|
||||||
c.mod.SetTarget(config.Triple)
|
c.mod.SetTarget(config.Triple)
|
||||||
c.mod.SetDataLayout(c.targetData.String())
|
c.mod.SetDataLayout(c.targetData.String())
|
||||||
if c.Debug {
|
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
||||||
if c.targetData.PointerSize() <= 4 {
|
if c.targetData.PointerSize() <= 4 {
|
||||||
@@ -185,7 +183,12 @@ 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
|
||||||
@@ -257,15 +260,13 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
ssaPkg.Build()
|
ssaPkg.Build()
|
||||||
|
|
||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
if c.Debug {
|
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||||
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
File: "<unknown>",
|
||||||
File: "<unknown>",
|
Dir: "",
|
||||||
Dir: "",
|
Producer: "TinyGo",
|
||||||
Producer: "TinyGo",
|
Optimized: true,
|
||||||
Optimized: true,
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load comments such as //go:extern on globals.
|
// Load comments such as //go:extern on globals.
|
||||||
c.loadASTComments(pkg)
|
c.loadASTComments(pkg)
|
||||||
@@ -280,23 +281,21 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
c.createPackage(irbuilder, ssaPkg)
|
c.createPackage(irbuilder, ssaPkg)
|
||||||
|
|
||||||
// see: https://reviews.llvm.org/D18355
|
// see: https://reviews.llvm.org/D18355
|
||||||
if c.Debug {
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
c.ctx.MDString("Debug Info Version"),
|
||||||
c.ctx.MDString("Debug Info Version"),
|
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
}),
|
||||||
}),
|
)
|
||||||
)
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
c.ctx.MDString("Dwarf Version"),
|
||||||
c.ctx.MDString("Dwarf Version"),
|
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
}),
|
||||||
}),
|
)
|
||||||
)
|
c.dibuilder.Finalize()
|
||||||
c.dibuilder.Finalize()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.mod, c.diagnostics
|
return c.mod, c.diagnostics
|
||||||
}
|
}
|
||||||
@@ -709,11 +708,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()) {
|
||||||
@@ -752,10 +751,13 @@ 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)
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -781,6 +783,9 @@ 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.
|
||||||
@@ -800,20 +805,18 @@ func (b *builder) createFunction() {
|
|||||||
b.llvmFn.AddFunctionAttr(noinline)
|
b.llvmFn.AddFunctionAttr(noinline)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add debug info, if needed.
|
// Add debug info.
|
||||||
if b.Debug {
|
if b.fn.Synthetic == "package initializer" {
|
||||||
if b.fn.Synthetic == "package initializer" {
|
// Package initializers have no debug info. Create some fake debug
|
||||||
// Package initializers have no debug info. Create some fake debug
|
// info to at least have *something*.
|
||||||
// info to at least have *something*.
|
filename := b.fn.Package().Pkg.Path() + "/<init>"
|
||||||
filename := b.fn.Package().Pkg.Path() + "/<init>"
|
b.difunc = b.attachDebugInfoRaw(b.fn, b.llvmFn, "", filename, 0)
|
||||||
b.difunc = b.attachDebugInfoRaw(b.fn, b.llvmFn, "", filename, 0)
|
} else if b.fn.Syntax() != nil {
|
||||||
} else if b.fn.Syntax() != nil {
|
// Create debug info file if needed.
|
||||||
// Create debug info file if needed.
|
b.difunc = b.attachDebugInfo(b.fn)
|
||||||
b.difunc = b.attachDebugInfo(b.fn)
|
|
||||||
}
|
|
||||||
pos := b.program.Fset.Position(b.fn.Pos())
|
|
||||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
|
||||||
}
|
}
|
||||||
|
pos := b.program.Fset.Position(b.fn.Pos())
|
||||||
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||||
|
|
||||||
// Pre-create all basic blocks in the function.
|
// Pre-create all basic blocks in the function.
|
||||||
for _, block := range b.fn.DomPreorder() {
|
for _, block := range b.fn.DomPreorder() {
|
||||||
@@ -838,7 +841,7 @@ func (b *builder) createFunction() {
|
|||||||
b.locals[param] = b.collapseFormalParam(llvmType, fields)
|
b.locals[param] = b.collapseFormalParam(llvmType, fields)
|
||||||
|
|
||||||
// Add debug information to this parameter (if available)
|
// Add debug information to this parameter (if available)
|
||||||
if b.Debug && b.fn.Syntax() != nil {
|
if b.fn.Syntax() != nil {
|
||||||
dbgParam := b.getLocalVariable(param.Object().(*types.Var))
|
dbgParam := b.getLocalVariable(param.Object().(*types.Var))
|
||||||
loc := b.GetCurrentDebugLocation()
|
loc := b.GetCurrentDebugLocation()
|
||||||
if len(fields) == 1 {
|
if len(fields) == 1 {
|
||||||
@@ -898,9 +901,6 @@ func (b *builder) createFunction() {
|
|||||||
b.currentBlock = block
|
b.currentBlock = block
|
||||||
for _, instr := range block.Instrs {
|
for _, instr := range block.Instrs {
|
||||||
if instr, ok := instr.(*ssa.DebugRef); ok {
|
if instr, ok := instr.(*ssa.DebugRef); ok {
|
||||||
if !b.Debug {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
object := instr.Object()
|
object := instr.Object()
|
||||||
variable, ok := object.(*types.Var)
|
variable, ok := object.(*types.Var)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -964,13 +964,61 @@ 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 {
|
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{})
|
|
||||||
}
|
|
||||||
|
|
||||||
switch instr := instr.(type) {
|
switch instr := instr.(type) {
|
||||||
case ssa.Value:
|
case ssa.Value:
|
||||||
@@ -994,52 +1042,8 @@ 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.
|
||||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
b.createGo(instr)
|
||||||
// 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()
|
||||||
@@ -1292,6 +1296,8 @@ 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"):
|
||||||
|
|||||||
+57
-38
@@ -19,6 +19,8 @@ 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 {
|
||||||
@@ -32,43 +34,56 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
target, err := compileopts.LoadTarget("i686--linux")
|
tests := []struct {
|
||||||
if err != nil {
|
file string
|
||||||
t.Fatal("failed to load target:", err)
|
target string
|
||||||
}
|
}{
|
||||||
config := &compileopts.Config{
|
{"basic.go", ""},
|
||||||
Options: &compileopts.Options{},
|
{"pointer.go", ""},
|
||||||
Target: target,
|
{"slice.go", ""},
|
||||||
}
|
{"string.go", ""},
|
||||||
compilerConfig := &Config{
|
{"float.go", ""},
|
||||||
Triple: config.Triple(),
|
{"interface.go", ""},
|
||||||
GOOS: config.GOOS(),
|
{"func.go", ""},
|
||||||
GOARCH: config.GOARCH(),
|
{"pragma.go", ""},
|
||||||
CodeModel: config.CodeModel(),
|
{"goroutine.go", "wasm"},
|
||||||
RelocationModel: config.RelocationModel(),
|
{"goroutine.go", "cortex-m-qemu"},
|
||||||
Scheduler: config.Scheduler(),
|
|
||||||
FuncImplementation: config.FuncImplementation(),
|
|
||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
|
||||||
}
|
|
||||||
machine, err := NewTargetMachine(compilerConfig)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("failed to create target machine:", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []string{
|
for _, tc := range tests {
|
||||||
"basic.go",
|
name := tc.file
|
||||||
"pointer.go",
|
targetString := "wasm"
|
||||||
"slice.go",
|
if tc.target != "" {
|
||||||
"string.go",
|
targetString = tc.target
|
||||||
"float.go",
|
name = tc.file + "-" + tc.target
|
||||||
"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/" + testCase}, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||||
Sizes: Sizes(machine),
|
Sizes: Sizes(machine),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -76,13 +91,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", testCase, err)
|
t.Fatalf("could not parse test case %s: %s", tc.file, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
pkg := lprogram.MainPkg()
|
pkg := lprogram.MainPkg()
|
||||||
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
mod, errs := CompilePackage(tc.file, 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)
|
||||||
@@ -105,18 +120,22 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
funcPasses.FinalizeFunc()
|
funcPasses.FinalizeFunc()
|
||||||
|
|
||||||
outfile := "./testdata/" + testCase[:len(testCase)-3] + ".ll"
|
outFilePrefix := tc.file[:len(tc.file)-3]
|
||||||
|
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(outfile, []byte(mod.String()), 0666)
|
err := ioutil.WriteFile(outPath, []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(outfile)
|
expected, err := ioutil.ReadFile(outPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to read golden file:", err)
|
t.Fatal("failed to read golden file:", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+157
-57
@@ -5,25 +5,110 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createGoInstruction starts a new goroutine with the provided function pointer
|
// createGo emits code to start a new goroutine.
|
||||||
// and parameters.
|
func (b *builder) createGo(instr *ssa.Go) {
|
||||||
// In general, you should pass all regular parameters plus the context parameter.
|
// Get all function parameters to pass to the goroutine.
|
||||||
// There is one exception: the task-based scheduler needs to have the function
|
var params []llvm.Value
|
||||||
// pointer passed in as a parameter too in addition to the context.
|
for _, param := range instr.Call.Args {
|
||||||
//
|
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, pos)
|
callee = b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.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
|
||||||
@@ -34,6 +119,9 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
|
|||||||
} 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":
|
||||||
@@ -46,7 +134,6 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
|
|||||||
}
|
}
|
||||||
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
|
||||||
@@ -67,7 +154,12 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
|
|||||||
// 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()
|
||||||
@@ -90,32 +182,38 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
if c.Debug {
|
// Add debug information.
|
||||||
pos := c.program.Fset.Position(pos)
|
pos := c.program.Fset.Position(pos)
|
||||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||||
File: c.getDIFile(pos.Filename),
|
File: c.getDIFile(pos.Filename),
|
||||||
Parameters: nil, // do not show parameters in debugger
|
Parameters: nil, // do not show parameters in debugger
|
||||||
Flags: 0, // ?
|
Flags: 0, // ?
|
||||||
})
|
})
|
||||||
difunc := c.dibuilder.CreateFunction(c.getDIFile(pos.Filename), llvm.DIFunction{
|
difunc := c.dibuilder.CreateFunction(c.getDIFile(pos.Filename), llvm.DIFunction{
|
||||||
Name: "<goroutine wrapper>",
|
Name: "<goroutine wrapper>",
|
||||||
File: c.getDIFile(pos.Filename),
|
File: c.getDIFile(pos.Filename),
|
||||||
Line: pos.Line,
|
Line: pos.Line,
|
||||||
Type: diFuncType,
|
Type: diFuncType,
|
||||||
LocalToUnit: true,
|
LocalToUnit: true,
|
||||||
IsDefinition: true,
|
IsDefinition: true,
|
||||||
ScopeLine: 0,
|
ScopeLine: 0,
|
||||||
Flags: llvm.FlagPrototyped,
|
Flags: llvm.FlagPrototyped,
|
||||||
Optimized: true,
|
Optimized: true,
|
||||||
})
|
})
|
||||||
wrapper.SetSubprogram(difunc)
|
wrapper.SetSubprogram(difunc)
|
||||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
}
|
|
||||||
|
|
||||||
// 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()
|
||||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes[:len(paramTypes)-1])
|
paramTypes = paramTypes[:len(paramTypes)-1] // strip parentHandle parameter
|
||||||
params = append(params, llvm.Undef(c.i8ptrType))
|
if !hasContext {
|
||||||
|
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
||||||
|
}
|
||||||
|
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
||||||
|
if !hasContext {
|
||||||
|
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
|
||||||
|
}
|
||||||
|
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy parentHandle parameter
|
||||||
|
|
||||||
// Create the call.
|
// Create the call.
|
||||||
builder.CreateCall(fn, params, "")
|
builder.CreateCall(fn, params, "")
|
||||||
@@ -147,27 +245,26 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
if c.Debug {
|
// Add debug information.
|
||||||
pos := c.program.Fset.Position(pos)
|
pos := c.program.Fset.Position(pos)
|
||||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||||
File: c.getDIFile(pos.Filename),
|
File: c.getDIFile(pos.Filename),
|
||||||
Parameters: nil, // do not show parameters in debugger
|
Parameters: nil, // do not show parameters in debugger
|
||||||
Flags: 0, // ?
|
Flags: 0, // ?
|
||||||
})
|
})
|
||||||
difunc := c.dibuilder.CreateFunction(c.getDIFile(pos.Filename), llvm.DIFunction{
|
difunc := c.dibuilder.CreateFunction(c.getDIFile(pos.Filename), llvm.DIFunction{
|
||||||
Name: "<goroutine wrapper>",
|
Name: "<goroutine wrapper>",
|
||||||
File: c.getDIFile(pos.Filename),
|
File: c.getDIFile(pos.Filename),
|
||||||
Line: pos.Line,
|
Line: pos.Line,
|
||||||
Type: diFuncType,
|
Type: diFuncType,
|
||||||
LocalToUnit: true,
|
LocalToUnit: true,
|
||||||
IsDefinition: true,
|
IsDefinition: true,
|
||||||
ScopeLine: 0,
|
ScopeLine: 0,
|
||||||
Flags: llvm.FlagPrototyped,
|
Flags: llvm.FlagPrototyped,
|
||||||
Optimized: true,
|
Optimized: true,
|
||||||
})
|
})
|
||||||
wrapper.SetSubprogram(difunc)
|
wrapper.SetSubprogram(difunc)
|
||||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
}
|
|
||||||
|
|
||||||
// Get the list of parameters, with the extra parameters at the end.
|
// Get the list of parameters, with the extra parameters at the end.
|
||||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
paramTypes := fn.Type().ElementType().ParamTypes()
|
||||||
@@ -177,8 +274,11 @@ 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]
|
||||||
|
|
||||||
// Ignore the last param, which isn't used anymore.
|
// The last parameter in the packed object has somewhat of a dual role.
|
||||||
// TODO: avoid this extra "parent handle" parameter in most functions.
|
// Inside the parameter bundle it's the function pointer, stored right
|
||||||
|
// after the context pointer. But in the IR call instruction, it's the
|
||||||
|
// parentHandle function that's always undef outside of the coroutines
|
||||||
|
// scheduler. Thus, make the parameter undef here.
|
||||||
params[len(params)-1] = llvm.Undef(c.i8ptrType)
|
params[len(params)-1] = llvm.Undef(c.i8ptrType)
|
||||||
|
|
||||||
// Create the call.
|
// Create the call.
|
||||||
|
|||||||
+17
-8
@@ -311,10 +311,21 @@ 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)
|
||||||
signatureGlobal := c.mod.NamedGlobal("func " + signature)
|
var globalName string
|
||||||
|
if token.IsExported(method.Name()) {
|
||||||
|
globalName = "reflect/methods." + signature
|
||||||
|
} else {
|
||||||
|
globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
|
||||||
|
}
|
||||||
|
signatureGlobal := c.mod.NamedGlobal(globalName)
|
||||||
if signatureGlobal.IsNil() {
|
if signatureGlobal.IsNil() {
|
||||||
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature)
|
// TODO: put something useful in these globals, such as the method
|
||||||
|
// signature. Useful to one day implement reflect.Value.Method(n).
|
||||||
|
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
|
||||||
|
signatureGlobal.SetInitializer(llvm.ConstInt(c.ctx.Int8Type(), 0, false))
|
||||||
|
signatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
signatureGlobal.SetGlobalConstant(true)
|
signatureGlobal.SetGlobalConstant(true)
|
||||||
|
signatureGlobal.SetAlignment(1)
|
||||||
}
|
}
|
||||||
return signatureGlobal
|
return signatureGlobal
|
||||||
}
|
}
|
||||||
@@ -490,12 +501,10 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
}
|
}
|
||||||
defer b.Builder.Dispose()
|
defer b.Builder.Dispose()
|
||||||
|
|
||||||
// add debug info if needed
|
// add debug info
|
||||||
if c.Debug {
|
pos := c.program.Fset.Position(fn.Pos())
|
||||||
pos := c.program.Fset.Position(fn.Pos())
|
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
|
||||||
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up IR builder
|
// set up IR builder
|
||||||
block := b.ctx.AddBasicBlock(wrapper, "entry")
|
block := b.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
|
|||||||
+12
-17
@@ -41,10 +41,7 @@ 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 := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
globalName := b.fn.Package().Pkg.Path() + "$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)
|
||||||
@@ -55,19 +52,17 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
|
|
||||||
// Add debug info to the interrupt global.
|
// Add debug info to the interrupt global.
|
||||||
if b.Debug {
|
pos := b.program.Fset.Position(instr.Pos())
|
||||||
pos := b.program.Fset.Position(instr.Pos())
|
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
|
||||||
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
|
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
||||||
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
LinkageName: globalName,
|
||||||
LinkageName: globalName,
|
File: b.getDIFile(pos.Filename),
|
||||||
File: b.getDIFile(pos.Filename),
|
Line: pos.Line,
|
||||||
Line: pos.Line,
|
Type: b.getDIType(globalType),
|
||||||
Type: b.getDIType(globalType),
|
Expr: b.dibuilder.CreateExpression(nil),
|
||||||
Expr: b.dibuilder.CreateExpression(nil),
|
LocalToUnit: false,
|
||||||
LocalToUnit: false,
|
})
|
||||||
})
|
global.AddMetadata(0, diglobal)
|
||||||
global.AddMetadata(0, diglobal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
|
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
|
||||||
// member of type int.
|
// member of type int.
|
||||||
|
|||||||
+11
-1
@@ -24,6 +24,7 @@ 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)
|
||||||
@@ -270,6 +271,10 @@ 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.
|
||||||
@@ -325,6 +330,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -380,7 +386,7 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
llvmGlobal.SetAlignment(alignment)
|
llvmGlobal.SetAlignment(alignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Debug && !info.extern {
|
if !info.extern {
|
||||||
// Add debug info.
|
// Add debug info.
|
||||||
pos := c.program.Fset.Position(g.Pos())
|
pos := c.program.Fset.Position(g.Pos())
|
||||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||||
@@ -438,6 +444,10 @@ 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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-8
@@ -10,11 +10,11 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createSyscall emits an inline system call instruction, depending on the
|
// createRawSyscall creates a system call with the provided system call number
|
||||||
// target OS/arch.
|
// and returns the result as a single integer (the system call result). The
|
||||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
// result is not further interpreted.
|
||||||
|
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) createSyscall(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)
|
||||||
syscallResult = b.CreateCall(target, args, "")
|
return b.CreateCall(target, args, ""), nil
|
||||||
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) createSyscall(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)
|
||||||
syscallResult = b.CreateCall(target, args, "")
|
return b.CreateCall(target, args, ""), nil
|
||||||
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) createSyscall(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)
|
||||||
syscallResult = b.CreateCall(target, args, "")
|
return b.CreateCall(target, args, ""), nil
|
||||||
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,10 +147,19 @@ func (b *builder) createSyscall(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)
|
||||||
syscallResult = b.CreateCall(target, args, "")
|
return b.CreateCall(target, args, ""), nil
|
||||||
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
|
||||||
@@ -190,3 +199,16 @@ 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
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -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-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
@@ -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-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
@@ -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-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||||
|
|
||||||
|
|||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
; ModuleID = 'goroutine.go'
|
||||||
|
source_filename = "goroutine.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
|
target triple = "armv7m-none-eabi"
|
||||||
|
|
||||||
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
|
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||||
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||||
|
%"internal/task.state" = type { i32, i32* }
|
||||||
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef, i8* undef)
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @main.regularFunction(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%unpack.int = ptrtoint i8* %0 to i32
|
||||||
|
call void @main.regularFunction(i32 %unpack.int, i8* undef, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*, i8*)
|
||||||
|
|
||||||
|
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef)
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%unpack.int = ptrtoint i8* %0 to i32
|
||||||
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%n = call i8* @runtime.alloc(i32 4, i8* undef, i8* null)
|
||||||
|
%0 = bitcast i8* %n to i32*
|
||||||
|
store i32 3, i32* %0, align 4
|
||||||
|
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
||||||
|
%2 = bitcast i8* %1 to i32*
|
||||||
|
store i32 5, i32* %2, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
store i8* %n, i8** %4, align 4
|
||||||
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef)
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef, i8* null)
|
||||||
|
%5 = load i32, i32* %0, align 4
|
||||||
|
call void @runtime.printint32(i32 %5, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%unpack.ptr = bitcast i8* %context to i32*
|
||||||
|
store i32 7, i32* %unpack.ptr, align 4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%1 = bitcast i8* %0 to i32*
|
||||||
|
%2 = load i32, i32* %1, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
%5 = load i8*, i8** %4, align 4
|
||||||
|
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.printint32(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.funcGoroutine(i8* %fn.context, void (i32, i8*, i8*)* %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call i8* @runtime.alloc(i32 12, i8* undef, i8* null)
|
||||||
|
%1 = bitcast i8* %0 to i32*
|
||||||
|
store i32 5, i32* %1, align 4
|
||||||
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%3 = bitcast i8* %2 to i8**
|
||||||
|
store i8* %fn.context, i8** %3, align 4
|
||||||
|
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%5 = bitcast i8* %4 to void (i32, i8*, i8*)**
|
||||||
|
store void (i32, i8*, i8*)* %fn.funcptr, void (i32, i8*, i8*)** %5, align 4
|
||||||
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef, i8* undef)
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #3 {
|
||||||
|
entry:
|
||||||
|
%1 = bitcast i8* %0 to i32*
|
||||||
|
%2 = load i32, i32* %1, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
%5 = load i8*, i8** %4, align 4
|
||||||
|
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%7 = bitcast i8* %6 to void (i32, i8*, i8*)**
|
||||||
|
%8 = load void (i32, i8*, i8*)*, void (i32, i8*, i8*)** %7, align 4
|
||||||
|
call void %8(i32 %2, i8* %5, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||||
|
|
||||||
|
attributes #0 = { "tinygo-gowrapper"="main.regularFunction" }
|
||||||
|
attributes #1 = { "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
|
attributes #2 = { "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
|
attributes #3 = { "tinygo-gowrapper" }
|
||||||
Vendored
+106
@@ -0,0 +1,106 @@
|
|||||||
|
; ModuleID = 'goroutine.go'
|
||||||
|
source_filename = "goroutine.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||||
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
|
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||||
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||||
|
%"internal/task.state" = type { i8* }
|
||||||
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
|
@"main.regularFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
||||||
|
@"main.inlineFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
||||||
|
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
||||||
|
@"main.closureFunctionGoroutine$1$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @main.regularFunction to i32), i8* bitcast ({ i32, i8* }* @"main.regularFunctionGoroutine$pack" to i8*), i32 undef, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @main.regularFunction(i32, i8*, i8*)
|
||||||
|
|
||||||
|
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.inlineFunctionGoroutine$1" to i32), i8* bitcast ({ i32, i8* }* @"main.inlineFunctionGoroutine$pack" to i8*), i32 undef, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%n = call i8* @runtime.alloc(i32 4, i8* undef, i8* null)
|
||||||
|
%0 = bitcast i8* %n to i32*
|
||||||
|
store i32 3, i32* %0, align 4
|
||||||
|
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
||||||
|
%2 = bitcast i8* %1 to i32*
|
||||||
|
store i32 5, i32* %2, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
store i8* %n, i8** %4, align 4
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* nonnull %1, i32 undef, i8* undef, i8* null)
|
||||||
|
%5 = load i32, i32* %0, align 4
|
||||||
|
call void @runtime.printint32(i32 %5, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%unpack.ptr = bitcast i8* %context to i32*
|
||||||
|
store i32 7, i32* %unpack.ptr, align 4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.printint32(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.funcGoroutine(i8* %fn.context, i32 %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call i32 @runtime.getFuncPtr(i8* %fn.context, i32 %fn.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null)
|
||||||
|
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
||||||
|
%2 = bitcast i8* %1 to i32*
|
||||||
|
store i32 5, i32* %2, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
store i8* %fn.context, i8** %4, align 4
|
||||||
|
call void @"internal/task.start"(i32 %0, i8* nonnull %1, i32 undef, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||||
Vendored
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func regularFunctionGoroutine() {
|
||||||
|
go regularFunction(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inlineFunctionGoroutine() {
|
||||||
|
go func(x int) {
|
||||||
|
}(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closureFunctionGoroutine() {
|
||||||
|
n := 3
|
||||||
|
go func(x int) {
|
||||||
|
n = 7
|
||||||
|
}(5)
|
||||||
|
print(n) // note: this is racy (but good enough for this test)
|
||||||
|
}
|
||||||
|
|
||||||
|
func funcGoroutine(fn func(x int)) {
|
||||||
|
go fn(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func recoverBuiltinGoroutine() {
|
||||||
|
// This is a no-op.
|
||||||
|
go recover()
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyBuiltinGoroutine(dst, src []byte) {
|
||||||
|
// This is not run in a goroutine. While this copy operation can indeed take
|
||||||
|
// some time (if there is a lot of data to copy), there is no race-free way
|
||||||
|
// to make use of the result so it's unlikely applications will make use of
|
||||||
|
// it. And doing it this way should be just within the Go specification.
|
||||||
|
go copy(dst, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeBuiltinGoroutine(ch chan int) {
|
||||||
|
// This builtin is executed directly, not in a goroutine.
|
||||||
|
// The observed behavior is the same.
|
||||||
|
go close(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regularFunction(x int)
|
||||||
Vendored
+8
-8
@@ -1,7 +1,7 @@
|
|||||||
; ModuleID = 'interface.go'
|
; ModuleID = 'interface.go'
|
||||||
source_filename = "interface.go"
|
source_filename = "interface.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
%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 = "i686--linux"
|
|||||||
@"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}}" }
|
||||||
@"func Error() string" = external constant i8
|
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
|
||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.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}}" }
|
||||||
@"func String() string" = external constant i8
|
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
|
||||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.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* @"func Error() string"]
|
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.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 @"func 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 @"reflect/methods.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
|
||||||
|
|||||||
Vendored
+2
-2
@@ -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-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
|||||||
Vendored
+66
@@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import _ "unsafe"
|
||||||
|
|
||||||
|
// Creates an external global with name extern_global.
|
||||||
|
//go:extern extern_global
|
||||||
|
var externGlobal [0]byte
|
||||||
|
|
||||||
|
// Creates a
|
||||||
|
//go:align 32
|
||||||
|
var alignedGlobal [4]uint32
|
||||||
|
|
||||||
|
// Test conflicting pragmas (the last one counts).
|
||||||
|
//go:align 64
|
||||||
|
//go:align 16
|
||||||
|
var alignedGlobal16 [4]uint32
|
||||||
|
|
||||||
|
// Test exported functions.
|
||||||
|
//export extern_func
|
||||||
|
func externFunc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a function in a different package using go:linkname.
|
||||||
|
//go:linkname withLinkageName1 somepkg.someFunction1
|
||||||
|
func withLinkageName1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import a function from a different package using go:linkname.
|
||||||
|
//go:linkname withLinkageName2 somepkg.someFunction2
|
||||||
|
func withLinkageName2()
|
||||||
|
|
||||||
|
// Function has an 'inline hint', similar to the inline keyword in C.
|
||||||
|
//go:inline
|
||||||
|
func inlineFunc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function should never be inlined, equivalent to GCC
|
||||||
|
// __attribute__((noinline)).
|
||||||
|
//go:noinline
|
||||||
|
func noinlineFunc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should have the specified section.
|
||||||
|
//go:section .special_function_section
|
||||||
|
func functionInSection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//export exportedFunctionInSection
|
||||||
|
//go:section .special_function_section
|
||||||
|
func exportedFunctionInSection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should not: it's only a declaration and not a definition.
|
||||||
|
//go:section .special_function_section
|
||||||
|
func undefinedFunctionNotInSection()
|
||||||
|
|
||||||
|
//go:section .special_global_section
|
||||||
|
var globalInSection uint32
|
||||||
|
|
||||||
|
//go:section .special_global_section
|
||||||
|
//go:extern undefinedGlobalNotInSection
|
||||||
|
var undefinedGlobalNotInSection uint32
|
||||||
|
|
||||||
|
//go:align 1024
|
||||||
|
//go:section .global_section
|
||||||
|
var multipleGlobalPragmas uint32
|
||||||
Vendored
+59
@@ -0,0 +1,59 @@
|
|||||||
|
; ModuleID = 'pragma.go'
|
||||||
|
source_filename = "pragma.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
@extern_global = external global [0 x i8], align 1
|
||||||
|
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
||||||
|
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
||||||
|
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
||||||
|
@undefinedGlobalNotInSection = external global i32, align 4
|
||||||
|
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @extern_func() #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @somepkg.someFunction1(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @somepkg.someFunction2(i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: inlinehint
|
||||||
|
define hidden void @main.inlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: noinline
|
||||||
|
define hidden void @main.noinlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.functionInSection(i8* %context, i8* %parentHandle) unnamed_addr section ".special_function_section" {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @exportedFunctionInSection() #3 section ".special_function_section" {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @main.undefinedFunctionNotInSection(i8*, i8*)
|
||||||
|
|
||||||
|
attributes #0 = { "wasm-export-name"="extern_func" }
|
||||||
|
attributes #1 = { inlinehint }
|
||||||
|
attributes #2 = { noinline }
|
||||||
|
attributes #3 = { "wasm-export-name"="exportedFunctionInSection" }
|
||||||
Vendored
+2
-2
@@ -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-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
@@ -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-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "i686--linux"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
%runtime._string = type { i8*, i32 }
|
%runtime._string = type { i8*, i32 }
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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.18.0-dev"
|
const Version = "0.20.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.
|
||||||
|
|||||||
+7
-1
@@ -129,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 _, obj := range r.objects {
|
for i, obj := range r.objects {
|
||||||
if obj.llvmGlobal.IsNil() {
|
if obj.llvmGlobal.IsNil() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -159,6 +159,12 @@ 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 {
|
||||||
|
|||||||
+22
-6
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -930,16 +931,31 @@ 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()
|
||||||
if len(indices) != 1 {
|
// Note: the Go LLVM API doesn't support multiple indices, so simulate
|
||||||
panic("expected exactly one index")
|
// this operation with some extra extractvalue instructions. Hopefully
|
||||||
|
// this is optimized to a single instruction.
|
||||||
|
agg := operands[0]
|
||||||
|
for i := 0; i < len(indices)-1; i++ {
|
||||||
|
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
|
||||||
}
|
}
|
||||||
result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
|
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
|
||||||
case llvm.InsertValue:
|
case llvm.InsertValue:
|
||||||
indices := inst.llvmInst.Indices()
|
indices := inst.llvmInst.Indices()
|
||||||
if len(indices) != 1 {
|
// Similar to extractvalue, we're working around a limitation in the Go
|
||||||
panic("expected exactly one index")
|
// LLVM API here by splitting the insertvalue into multiple instructions
|
||||||
|
// if there is more than one operand.
|
||||||
|
agg := operands[0]
|
||||||
|
aggregates := []llvm.Value{agg}
|
||||||
|
for i := 0; i < len(indices)-1; i++ {
|
||||||
|
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
|
||||||
|
aggregates = append(aggregates, agg)
|
||||||
}
|
}
|
||||||
result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
|
result = operands[1]
|
||||||
|
for i := len(indices) - 1; i >= 0; i-- {
|
||||||
|
agg := aggregates[i]
|
||||||
|
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
|
||||||
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:
|
||||||
|
|||||||
@@ -178,6 +178,9 @@ 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() {
|
||||||
|
|||||||
Vendored
+22
@@ -8,6 +8,7 @@ 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
|
||||||
|
|
||||||
@@ -65,12 +66,22 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +105,15 @@ 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.
|
||||||
@@ -112,3 +132,5 @@ two:
|
|||||||
otherwise:
|
otherwise:
|
||||||
ret i64 -1
|
ret i64 -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare {i8, i32, {float, {i64, i16}}} @nestedStruct()
|
||||||
|
|||||||
Vendored
+21
@@ -7,6 +7,7 @@ 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
|
||||||
|
|
||||||
@@ -25,8 +26,20 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +60,12 @@ 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 [
|
||||||
@@ -67,3 +86,5 @@ 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
|
||||||
|
|||||||
+1
-1
Submodule lib/cmsis-svd updated: 2fb66c5735...9c35b6d9df
@@ -23,3 +23,13 @@ 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"
|
||||||
|
}
|
||||||
|
|||||||
+37
-15
@@ -2,6 +2,14 @@ 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"
|
||||||
@@ -139,6 +147,7 @@ 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.
|
||||||
@@ -154,6 +163,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
hasTinyGoFiles = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,21 +174,30 @@ 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() {
|
||||||
// Don't merge in files from Go. Otherwise we'd end up with a
|
if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok {
|
||||||
// weird syscall package with files from both roots.
|
// Already included above, so don't bother trying to create this
|
||||||
continue
|
// symlink.
|
||||||
}
|
continue
|
||||||
if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok {
|
}
|
||||||
// Already included above, so don't bother trying to create this
|
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
||||||
// symlink.
|
oldname := filepath.Join(goroot, "src", importPath, e.Name())
|
||||||
continue
|
err := symlink(oldname, newname)
|
||||||
}
|
if err != nil {
|
||||||
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
return err
|
||||||
oldname := filepath.Join(goroot, "src", importPath, e.Name())
|
}
|
||||||
err := symlink(oldname, newname)
|
} else {
|
||||||
if err != nil {
|
// Only merge files from Go if TinyGo does not have any files.
|
||||||
return err
|
// Otherwise we'd end up with a weird mix from both Go
|
||||||
|
// implementations.
|
||||||
|
if !hasTinyGoFiles {
|
||||||
|
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
|
||||||
|
oldname := filepath.Join(goroot, "src", importPath, e.Name())
|
||||||
|
err := symlink(oldname, newname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,6 +220,8 @@ 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,
|
||||||
@@ -208,6 +229,7 @@ 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,
|
||||||
|
|||||||
+11
-6
@@ -210,6 +210,12 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,12 +343,11 @@ func (p *Package) Check() error {
|
|||||||
checker.Importer = p
|
checker.Importer = p
|
||||||
|
|
||||||
packageName := p.ImportPath
|
packageName := p.ImportPath
|
||||||
if p.Name == "main" {
|
if p == p.program.MainPkg() {
|
||||||
// The main package normally has a different import path, such as
|
if p.Name != "main" {
|
||||||
// "command-line-arguments" or "./testdata/cgo". Therefore, use the name
|
// Sanity check. Should not ever trigger.
|
||||||
// "main" in such a case: this package isn't imported from anywhere.
|
panic("expected main package to have name 'main'")
|
||||||
// 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)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -95,12 +96,31 @@ 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 {
|
if options.PrintCommands != nil {
|
||||||
fmt.Printf("%s %s\n", name, strings.Join(arg, " "))
|
options.PrintCommands(name, 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)
|
||||||
@@ -136,15 +156,17 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test runs the tests in the given package.
|
// Test runs the tests in the given package. Returns whether the test passed and
|
||||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) error {
|
// possibly an error if the test failed to run.
|
||||||
|
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 err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
passed := true
|
||||||
|
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 == "" {
|
||||||
@@ -158,48 +180,78 @@ 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 directly.
|
// Run the test.
|
||||||
cmd := executeCommand(config.Options, result.Binary)
|
start := time.Now()
|
||||||
cmd.Stdout = os.Stdout
|
var err error
|
||||||
cmd.Stderr = os.Stderr
|
passed, err = runPackageTest(config, result)
|
||||||
cmd.Dir = result.MainDir
|
if err != nil {
|
||||||
err := cmd.Run()
|
return err
|
||||||
if err != nil {
|
}
|
||||||
// Propagate the exit code
|
duration := time.Since(start)
|
||||||
if err, ok := err.(*exec.ExitError); ok {
|
|
||||||
os.Exit(err.ExitCode())
|
// Print the result.
|
||||||
}
|
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
if passed {
|
||||||
}
|
fmt.Printf("ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
// Run in an emulator.
|
fmt.Printf("FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||||
args := append(config.Target.Emulator[1:], result.Binary)
|
}
|
||||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
return nil
|
||||||
buf := &bytes.Buffer{}
|
})
|
||||||
w := io.MultiWriter(os.Stdout, buf)
|
if err, ok := err.(loader.NoTestFilesError); ok {
|
||||||
cmd.Stdout = w
|
fmt.Printf("? \t%s\t[no test files]\n", err.ImportPath)
|
||||||
cmd.Stderr = os.Stderr
|
// Pretend the test passed - it at least didn't fail.
|
||||||
err := cmd.Run()
|
return true, nil
|
||||||
if err != nil {
|
}
|
||||||
if err, ok := err.(*exec.ExitError); !ok || !err.Exited() {
|
return passed, err
|
||||||
// Workaround for QEMU which always exits with an error.
|
}
|
||||||
return &commandError{"failed to run emulator with", result.Binary, 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
|
||||||
}
|
}
|
||||||
testOutput := string(buf.Bytes())
|
return false, &commandError{"failed to run compiled binary", result.Binary, err}
|
||||||
if testOutput == "PASS\n" || strings.HasSuffix(testOutput, "\nPASS\n") {
|
}
|
||||||
// Test passed.
|
return true, nil
|
||||||
return nil
|
} else {
|
||||||
} else {
|
// Run in an emulator.
|
||||||
// Test failed, either by ending with the word "FAIL" or with a
|
args := append(config.Target.Emulator[1:], result.Binary)
|
||||||
// panic of some sort.
|
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||||
os.Exit(1)
|
buf := &bytes.Buffer{}
|
||||||
return nil // unreachable
|
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.
|
||||||
@@ -224,6 +276,8 @@ 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?")
|
||||||
}
|
}
|
||||||
@@ -243,7 +297,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(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
port, err := getDefaultPort(port, config.Target.SerialPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -261,36 +315,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
|
||||||
fileToken := "{" + fileExt[1:] + "}"
|
flashCmdList, err := shlex.Split(flashCmd)
|
||||||
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
|
if err != nil {
|
||||||
|
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(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
port, err = getDefaultPort(port, config.Target.SerialPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flashCmd = strings.ReplaceAll(flashCmd, "{port}", port)
|
// Fill in fields in the command template.
|
||||||
|
fileToken := "{" + fileExt[1:] + "}"
|
||||||
// Execute the command.
|
for i, arg := range flashCmdList {
|
||||||
var cmd *exec.Cmd
|
arg = strings.ReplaceAll(arg, fileToken, result.Binary)
|
||||||
switch runtime.GOOS {
|
arg = strings.ReplaceAll(arg, "{port}", port)
|
||||||
case "windows":
|
flashCmdList[i] = arg
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the command.
|
||||||
|
if len(flashCmdList) < 2 {
|
||||||
|
return fmt.Errorf("invalid flash command: %#v", 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}
|
||||||
}
|
}
|
||||||
@@ -381,7 +435,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 remote :3333", "monitor halt", "load", "monitor reset halt")
|
gdbCommands = append(gdbCommands, "target extended-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()
|
||||||
@@ -400,7 +454,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 remote :2331", "load", "monitor reset halt")
|
gdbCommands = append(gdbCommands, "target extended-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)
|
||||||
@@ -415,7 +469,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 remote :1234")
|
gdbCommands = append(gdbCommands, "target extended-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")
|
||||||
@@ -423,7 +477,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 remote :1234")
|
gdbCommands = append(gdbCommands, "target extended-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)
|
||||||
@@ -431,7 +485,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 remote :2345")
|
gdbCommands = append(gdbCommands, "target extended-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")
|
||||||
@@ -439,7 +493,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 remote :1234")
|
gdbCommands = append(gdbCommands, "target extended-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)
|
||||||
@@ -665,7 +719,8 @@ 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(portCandidates []string) (port string, err error) {
|
func getDefaultPort(portFlag string, usbInterfaces []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
|
||||||
}
|
}
|
||||||
@@ -681,11 +736,70 @@ func getDefaultPort(portCandidates []string) (port string, err error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range portsList {
|
var preferredPortIDs [][2]uint16
|
||||||
ports = append(ports, p.Name)
|
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)})
|
||||||
}
|
}
|
||||||
|
|
||||||
if ports == nil || len(ports) == 0 {
|
var primaryPorts []string // ports picked from preferred USB VID/PID
|
||||||
|
var secondaryPorts []string // other ports (as a fallback)
|
||||||
|
for _, p := range portsList {
|
||||||
|
if !p.IsUSB {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.VID != "" && p.PID != "" {
|
||||||
|
foundPort := false
|
||||||
|
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
|
||||||
|
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
|
||||||
|
if vidErr == nil && pidErr == nil {
|
||||||
|
for _, id := range preferredPortIDs {
|
||||||
|
if uint16(vid) == id[0] && uint16(pid) == id[1] {
|
||||||
|
primaryPorts = append(primaryPorts, p.Name)
|
||||||
|
foundPort = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundPort {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secondaryPorts = append(secondaryPorts, p.Name)
|
||||||
|
}
|
||||||
|
if len(primaryPorts) == 1 {
|
||||||
|
// There is exactly one match in the set of preferred ports. Use
|
||||||
|
// this port, even if there may be others available. This allows
|
||||||
|
// flashing a specific board even if there are multiple available.
|
||||||
|
return primaryPorts[0], nil
|
||||||
|
} else if len(primaryPorts) > 1 {
|
||||||
|
// There are multiple preferred ports, probably because more than
|
||||||
|
// one device of the same type are connected (e.g. two Arduino
|
||||||
|
// Unos).
|
||||||
|
ports = primaryPorts
|
||||||
|
} else {
|
||||||
|
// No preferred ports found. Fall back to other serial ports
|
||||||
|
// available in the system.
|
||||||
|
ports = secondaryPorts
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ports) == 0 {
|
||||||
// fallback
|
// fallback
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
@@ -895,6 +1009,7 @@ 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")
|
||||||
@@ -904,17 +1019,20 @@ func main() {
|
|||||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
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")
|
debug := flag.String("debug", "auto", "remove debug information (auto, true, false)")
|
||||||
|
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 *bool
|
var flagJSON, flagDeps, flagTest *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, "")
|
flagDeps = flag.Bool("deps", false, "supply -deps flag to go list")
|
||||||
|
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" {
|
||||||
@@ -943,6 +1061,7 @@ 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)
|
||||||
@@ -951,24 +1070,35 @@ 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,
|
||||||
PrintIR: *printIR,
|
Serial: *serial,
|
||||||
DumpSSA: *dumpSSA,
|
PrintIR: *printIR,
|
||||||
VerifyIR: *verifyIR,
|
DumpSSA: *dumpSSA,
|
||||||
Debug: !*nodebug,
|
VerifyIR: *verifyIR,
|
||||||
PrintSizes: *printSize,
|
Debug: *debug,
|
||||||
PrintStacks: *printStacks,
|
PrintSizes: *printSize,
|
||||||
PrintAllocs: printAllocs,
|
PrintStacks: *printStacks,
|
||||||
PrintCommands: *printCommands,
|
PrintAllocs: printAllocs,
|
||||||
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)
|
||||||
@@ -1043,11 +1173,6 @@ func main() {
|
|||||||
err := Flash(pkgName, *port, options)
|
err := Flash(pkgName, *port, options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
} else {
|
} else {
|
||||||
if !options.Debug {
|
|
||||||
fmt.Fprintln(os.Stderr, "Debug disabled while running gdb?")
|
|
||||||
usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
err := FlashGDB(pkgName, *ocdOutput, options)
|
err := FlashGDB(pkgName, *ocdOutput, options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
}
|
}
|
||||||
@@ -1061,16 +1186,26 @@ func main() {
|
|||||||
err := Run(pkgName, options)
|
err := Run(pkgName, options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
case "test":
|
case "test":
|
||||||
pkgName := "."
|
var pkgNames []string
|
||||||
if flag.NArg() == 1 {
|
for i := 0; i < flag.NArg(); i++ {
|
||||||
pkgName = filepath.ToSlash(flag.Arg(0))
|
pkgNames = append(pkgNames, filepath.ToSlash(flag.Arg(i)))
|
||||||
} else if flag.NArg() > 1 {
|
}
|
||||||
fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
|
if len(pkgNames) == 0 {
|
||||||
usage()
|
pkgNames = []string{"."}
|
||||||
|
}
|
||||||
|
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)
|
||||||
@@ -1145,6 +1280,9 @@ 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)
|
||||||
|
|||||||
+2
-2
@@ -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",
|
||||||
@@ -194,7 +194,7 @@ func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []strin
|
|||||||
PrintIR: false,
|
PrintIR: false,
|
||||||
DumpSSA: false,
|
DumpSSA: false,
|
||||||
VerifyIR: true,
|
VerifyIR: true,
|
||||||
Debug: true,
|
EmitDWARF: true,
|
||||||
PrintSizes: "",
|
PrintSizes: "",
|
||||||
WasmAbi: "",
|
WasmAbi: "",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// +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
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// +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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-6
@@ -26,12 +26,21 @@ 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)))
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// +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()
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// +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()
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/sam"
|
||||||
|
"fmt"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type canMsg struct {
|
||||||
|
ch byte
|
||||||
|
id uint32
|
||||||
|
dlc byte
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan canMsg, 10)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case m := <-ch:
|
||||||
|
fmt.Printf("%d %03X %X ", m.ch, m.id, m.dlc)
|
||||||
|
for _, d := range m.data {
|
||||||
|
fmt.Printf("%02X ", d)
|
||||||
|
}
|
||||||
|
fmt.Printf("\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
can1 := machine.CAN1
|
||||||
|
can1.Configure(machine.CANConfig{
|
||||||
|
TransferRate: machine.CANTransferRate500kbps,
|
||||||
|
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||||
|
Rx: machine.CAN1_RX,
|
||||||
|
Tx: machine.CAN1_TX,
|
||||||
|
Standby: machine.CAN1_STANDBY,
|
||||||
|
})
|
||||||
|
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||||
|
can1.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||||
|
rxMsg := machine.CANRxBufferElement{}
|
||||||
|
can1.RxRaw(&rxMsg)
|
||||||
|
m := canMsg{ch: 1, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||||
|
select {
|
||||||
|
case ch <- m:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
can0 := machine.CAN0
|
||||||
|
can0.Configure(machine.CANConfig{
|
||||||
|
TransferRate: machine.CANTransferRate500kbps,
|
||||||
|
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||||
|
Rx: machine.CAN0_RX,
|
||||||
|
Tx: machine.CAN0_TX,
|
||||||
|
Standby: machine.NoPin,
|
||||||
|
})
|
||||||
|
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||||
|
can0.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||||
|
rxMsg := machine.CANRxBufferElement{}
|
||||||
|
can0.RxRaw(&rxMsg)
|
||||||
|
m := canMsg{ch: 2, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||||
|
select {
|
||||||
|
case ch <- m:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
for {
|
||||||
|
can0.Tx(0x123, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, false, false)
|
||||||
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
can1.Tx(0x456, []byte{0xAA, 0xBB, 0xCC}, false, false)
|
||||||
|
time.Sleep(time.Millisecond * 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// change these to test a different UART or pins if available
|
// change these to test a different UART or pins if available
|
||||||
var (
|
var (
|
||||||
uart = machine.UART0
|
uart = machine.Serial
|
||||||
tx = machine.UART_TX_PIN
|
tx = machine.UART_TX_PIN
|
||||||
rx = machine.UART_RX_PIN
|
rx = machine.UART_RX_PIN
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// +build stm32
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
const (
|
||||||
|
buttonMode = machine.PinInputPulldown
|
||||||
|
buttonPinChange = machine.PinRising | machine.PinFalling
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build bluepill
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = &machine.TIM2
|
||||||
|
pinA = machine.PA0
|
||||||
|
pinB = machine.PA1
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build stm32f7
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = &machine.TIM1
|
||||||
|
pinA = machine.PA8
|
||||||
|
pinB = machine.PA9
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build stm32l0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = &machine.TIM2
|
||||||
|
pinA = machine.PA0
|
||||||
|
pinB = machine.PB3
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build stm32l4
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = &machine.TIM2
|
||||||
|
pinA = machine.PA0
|
||||||
|
pinB = machine.PB3
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build stm32l5
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = &machine.TIM1
|
||||||
|
pinA = machine.PA8
|
||||||
|
pinB = machine.PA9
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// +build stm32f4disco
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These pins correspond to LEDs on the discovery
|
||||||
|
// board
|
||||||
|
pwm = &machine.TIM4
|
||||||
|
pinA = machine.PD12
|
||||||
|
pinB = machine.PD13
|
||||||
|
)
|
||||||
@@ -13,7 +13,7 @@ type Task struct {
|
|||||||
Ptr unsafe.Pointer
|
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 uint
|
Data uint64
|
||||||
|
|
||||||
// state is the underlying running state of the task.
|
// state is the underlying running state of the task.
|
||||||
state state
|
state state
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
.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
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// +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
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#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
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// +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
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// 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}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// +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
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
.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
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// +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
|
||||||
|
}
|
||||||
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
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"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const (
|
|||||||
LED = D6
|
LED = D6
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 aka USBCDC pins
|
// USBCDC pins
|
||||||
const (
|
const (
|
||||||
USBCDC_DM_PIN Pin = PA24
|
USBCDC_DM_PIN Pin = PA24
|
||||||
USBCDC_DP_PIN Pin = PA25
|
USBCDC_DP_PIN Pin = PA25
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const (
|
|||||||
LED = D13
|
LED = D13
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 aka USBCDC pins
|
// 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,7 +9,8 @@ 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 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM3_USART,
|
Bus: sam.SERCOM3_USART,
|
||||||
SERCOM: 3,
|
SERCOM: 3,
|
||||||
@@ -18,7 +19,8 @@ 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 = UART{
|
UART2 = &_UART2
|
||||||
|
_UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM5_USART,
|
Bus: sam.SERCOM5_USART,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
@@ -26,8 +28,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.
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ const (
|
|||||||
PIN_USB_ID = PC19
|
PIN_USB_ID = PC19
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 aka USBCDC pins
|
// USBCDC pins
|
||||||
const (
|
const (
|
||||||
USBCDC_DM_PIN = PA24
|
USBCDC_DM_PIN = PA24
|
||||||
USBCDC_DP_PIN = PA25
|
USBCDC_DP_PIN = PA25
|
||||||
@@ -240,28 +240,32 @@ var (
|
|||||||
// UART on the SAM E54 Xplained Pro
|
// UART on the SAM E54 Xplained Pro
|
||||||
var (
|
var (
|
||||||
// Extension Header EXT1
|
// Extension Header EXT1
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_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 = UART{
|
UART2 = &_UART2
|
||||||
|
_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 = UART{
|
UART3 = &_UART3
|
||||||
|
_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 = UART{
|
UART4 = &_UART4
|
||||||
|
_UART4 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM2_USART_INT,
|
Bus: sam.SERCOM2_USART_INT,
|
||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
@@ -269,10 +273,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
|
||||||
@@ -328,3 +332,14 @@ var (
|
|||||||
SERCOM: 6,
|
SERCOM: 6,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CAN on the SAM E54 Xplained Pro
|
||||||
|
var (
|
||||||
|
CAN0 = CAN{
|
||||||
|
Bus: sam.CAN0,
|
||||||
|
}
|
||||||
|
|
||||||
|
CAN1 = CAN{
|
||||||
|
Bus: sam.CAN1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,49 +7,18 @@ 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
|
||||||
@@ -60,20 +29,21 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// USART1 is the first hardware serial port on the STM32.
|
// USART1 is the first hardware serial port on the STM32.
|
||||||
// Both UART0 and UART1 refer to USART1.
|
UART1 = &_UART1
|
||||||
UART0 = UART{
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART1,
|
Bus: stm32.USART1,
|
||||||
}
|
}
|
||||||
UART1 = UART{
|
UART2 = &_UART2
|
||||||
|
_UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART2,
|
Bus: stm32.USART2,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART1.handleInterrupt)
|
||||||
UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, UART1.handleInterrupt)
|
UART2.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART2.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const (
|
|||||||
const (
|
const (
|
||||||
LED = D13
|
LED = D13
|
||||||
NEOPIXELS = D8
|
NEOPIXELS = D8
|
||||||
|
WS2812 = D8
|
||||||
|
|
||||||
BUTTONA = D4
|
BUTTONA = D4
|
||||||
BUTTONB = D5
|
BUTTONB = D5
|
||||||
@@ -56,11 +57,6 @@ 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
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const (
|
|||||||
const (
|
const (
|
||||||
LED = D13
|
LED = D13
|
||||||
NEOPIXELS = D8
|
NEOPIXELS = D8
|
||||||
|
WS2812 = D8
|
||||||
|
|
||||||
BUTTONA = D4
|
BUTTONA = D4
|
||||||
BUTTONB = D5
|
BUTTONB = D5
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import (
|
|||||||
|
|
||||||
// UART1 on the Circuit Playground Express.
|
// UART1 on the Circuit Playground Express.
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM4_USART,
|
Bus: sam.SERCOM4_USART,
|
||||||
SERCOM: 4,
|
SERCOM: 4,
|
||||||
@@ -17,7 +18,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4, _UART1.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on the Circuit Playground Express.
|
// I2C on the Circuit Playground Express.
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ const (
|
|||||||
LED1 = LED
|
LED1 = LED
|
||||||
LED2 = D43
|
LED2 = D43
|
||||||
NEOPIXEL = D18
|
NEOPIXEL = D18
|
||||||
|
WS2812 = D18
|
||||||
|
|
||||||
BUTTON_LEFT = D5
|
BUTTON_LEFT = D5
|
||||||
BUTTON_RIGHT = D11
|
BUTTON_RIGHT = D11
|
||||||
@@ -103,11 +104,6 @@ const (
|
|||||||
UART_TX_PIN = D1
|
UART_TX_PIN = D1
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 is the USB device
|
|
||||||
var (
|
|
||||||
UART0 = &USB
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = D20 // I2C0 external
|
SDA_PIN = D20 // I2C0 external
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const (
|
|||||||
LED = D13
|
LED = D13
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 aka USBCDC pins
|
// USBCDC pins
|
||||||
const (
|
const (
|
||||||
USBCDC_DM_PIN = PA24
|
USBCDC_DM_PIN = PA24
|
||||||
USBCDC_DP_PIN = PA25
|
USBCDC_DP_PIN = PA25
|
||||||
@@ -56,7 +56,8 @@ const (
|
|||||||
|
|
||||||
// UART1 on the Feather M0.
|
// UART1 on the Feather M0.
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM1_USART,
|
Bus: sam.SERCOM1_USART,
|
||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
@@ -64,7 +65,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, _UART1.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
|
|||||||
@@ -44,9 +44,10 @@ const (
|
|||||||
const (
|
const (
|
||||||
LED = D13
|
LED = D13
|
||||||
NEOPIXELS = D8
|
NEOPIXELS = D8
|
||||||
|
WS2812 = D8
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 aka USBCDC pins
|
// USBCDC pins
|
||||||
const (
|
const (
|
||||||
USBCDC_DM_PIN = PA24
|
USBCDC_DM_PIN = PA24
|
||||||
USBCDC_DP_PIN = PA25
|
USBCDC_DP_PIN = PA25
|
||||||
@@ -103,13 +104,15 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM5_USART_INT,
|
Bus: sam.SERCOM5_USART_INT,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
UART2 = UART{
|
UART2 = &_UART2
|
||||||
|
_UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM0_USART_INT,
|
Bus: sam.SERCOM0_USART_INT,
|
||||||
SERCOM: 0,
|
SERCOM: 0,
|
||||||
@@ -117,15 +120,15 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, _UART1.handleInterrupt)
|
||||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, _UART2.handleInterrupt)
|
||||||
|
|
||||||
// turn on neopixel
|
// turn on neopixel
|
||||||
D7.Configure(PinConfig{Mode: PinOutput})
|
D7.Configure(PinConfig{Mode: PinOutput})
|
||||||
D7.High()
|
D7.High()
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on the Feather M4.
|
// I2C on the Feather M4 CAN.
|
||||||
var (
|
var (
|
||||||
I2C0 = &I2C{
|
I2C0 = &I2C{
|
||||||
Bus: sam.SERCOM2_I2CM,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
@@ -133,10 +136,21 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI on the Feather M4.
|
// SPI on the Feather M4 CAN.
|
||||||
var (
|
var (
|
||||||
SPI0 = SPI{
|
SPI0 = SPI{
|
||||||
Bus: sam.SERCOM1_SPIM,
|
Bus: sam.SERCOM1_SPIM,
|
||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CAN on the Feather M4 CAN.
|
||||||
|
var (
|
||||||
|
CAN0 = CAN{
|
||||||
|
Bus: sam.CAN0,
|
||||||
|
}
|
||||||
|
|
||||||
|
CAN1 = CAN{
|
||||||
|
Bus: sam.CAN1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -36,10 +36,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LED = D13
|
LED = D13
|
||||||
|
WS2812 = D8
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 aka USBCDC pins
|
// USBCDC pins
|
||||||
const (
|
const (
|
||||||
USBCDC_DM_PIN = PA24
|
USBCDC_DM_PIN = PA24
|
||||||
USBCDC_DP_PIN = PA25
|
USBCDC_DP_PIN = PA25
|
||||||
|
|||||||
@@ -8,13 +8,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM5_USART_INT,
|
Bus: sam.SERCOM5_USART_INT,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
UART2 = UART{
|
UART2 = &_UART2
|
||||||
|
_UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM0_USART_INT,
|
Bus: sam.SERCOM0_USART_INT,
|
||||||
SERCOM: 0,
|
SERCOM: 0,
|
||||||
@@ -22,8 +24,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, _UART1.handleInterrupt)
|
||||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, _UART2.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on the Feather M4.
|
// I2C on the Feather M4.
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// +build feather_nrf52840_sense
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const HasLowFrequencyCrystal = true
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
D0 = P0_25 // UART TX
|
||||||
|
D1 = P0_24 // UART RX
|
||||||
|
D2 = P0_10 // NFC2
|
||||||
|
D3 = P1_11
|
||||||
|
D4 = P1_10 // LED2
|
||||||
|
D5 = P1_08
|
||||||
|
D6 = P0_07
|
||||||
|
D7 = P1_02 // Button
|
||||||
|
D8 = P0_16 // NeoPixel
|
||||||
|
D9 = P0_26
|
||||||
|
D10 = P0_27
|
||||||
|
D11 = P0_06
|
||||||
|
D12 = P0_08
|
||||||
|
D13 = P1_09 // LED1
|
||||||
|
D14 = P0_04 // A0
|
||||||
|
D15 = P0_05 // A1
|
||||||
|
D16 = P0_30 // A2
|
||||||
|
D17 = P0_28 // A3
|
||||||
|
D18 = P0_02 // A4
|
||||||
|
D19 = P0_03 // A5
|
||||||
|
D20 = P0_29 // Battery
|
||||||
|
D21 = P0_31 // AREF
|
||||||
|
D22 = P0_12 // I2C SDA
|
||||||
|
D23 = P0_11 // I2C SCL
|
||||||
|
D24 = P0_15 // SPI MISO
|
||||||
|
D25 = P0_13 // SPI MOSI
|
||||||
|
D26 = P0_14 // SPI SCK
|
||||||
|
D27 = P0_19 // QSPI CLK
|
||||||
|
D28 = P0_20 // QSPI CS
|
||||||
|
D29 = P0_17 // QSPI Data 0
|
||||||
|
D30 = P0_22 // QSPI Data 1
|
||||||
|
D31 = P0_23 // QSPI Data 2
|
||||||
|
D32 = P0_21 // QSPI Data 3
|
||||||
|
D33 = P0_09 // NFC1 (test point on bottom of board)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog Pins
|
||||||
|
const (
|
||||||
|
A0 = D14
|
||||||
|
A1 = D15
|
||||||
|
A2 = D16
|
||||||
|
A3 = D17
|
||||||
|
A4 = D18
|
||||||
|
A5 = D19
|
||||||
|
A6 = D20 // Battery
|
||||||
|
A7 = D21 // ARef
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = D13
|
||||||
|
LED1 = LED
|
||||||
|
LED2 = D4
|
||||||
|
NEOPIXEL = D8
|
||||||
|
WS2812 = D8
|
||||||
|
BUTTON = D7
|
||||||
|
|
||||||
|
QSPI_SCK = D27
|
||||||
|
QSPI_CS = D28
|
||||||
|
QSPI_DATA0 = D29
|
||||||
|
QSPI_DATA1 = D30
|
||||||
|
QSPI_DATA2 = D31
|
||||||
|
QSPI_DATA3 = D32
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 pins (logical UART1)
|
||||||
|
const (
|
||||||
|
UART_RX_PIN = D0
|
||||||
|
UART_TX_PIN = D1
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
SDA_PIN = D22 // I2C0 external
|
||||||
|
SCL_PIN = D23 // I2C0 external
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = D26 // SCK
|
||||||
|
SPI0_SDO_PIN = D25 // SDO
|
||||||
|
SPI0_SDI_PIN = D24 // SDI
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "Feather nRF52840 Express"
|
||||||
|
usb_STRING_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x239A
|
||||||
|
usb_PID uint16 = 0x8088
|
||||||
|
)
|
||||||
@@ -59,6 +59,7 @@ const (
|
|||||||
LED1 = LED
|
LED1 = LED
|
||||||
LED2 = D4
|
LED2 = D4
|
||||||
NEOPIXEL = D8
|
NEOPIXEL = D8
|
||||||
|
WS2812 = D8
|
||||||
BUTTON = D7
|
BUTTON = D7
|
||||||
|
|
||||||
QSPI_SCK = D27
|
QSPI_SCK = D27
|
||||||
@@ -75,11 +76,6 @@ const (
|
|||||||
UART_TX_PIN = D1
|
UART_TX_PIN = D1
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 is the USB device
|
|
||||||
var (
|
|
||||||
UART0 = &USB
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = D22 // I2C0 external
|
SDA_PIN = D22 // I2C0 external
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ const (
|
|||||||
LED_NEOPIXEL = D8
|
LED_NEOPIXEL = D8
|
||||||
LED_BUILTIN = LED_RED
|
LED_BUILTIN = LED_RED
|
||||||
LED = LED_BUILTIN
|
LED = LED_BUILTIN
|
||||||
|
WS2812 = D8
|
||||||
)
|
)
|
||||||
|
|
||||||
func initLED() {}
|
func initLED() {}
|
||||||
@@ -119,31 +120,34 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART3,
|
Bus: stm32.USART3,
|
||||||
TxAltFuncSelector: AF7_USART1_2_3,
|
TxAltFuncSelector: AF7_USART1_2_3,
|
||||||
RxAltFuncSelector: AF7_USART1_2_3,
|
RxAltFuncSelector: AF7_USART1_2_3,
|
||||||
}
|
}
|
||||||
UART2 = UART{
|
UART2 = &_UART2
|
||||||
|
_UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART6,
|
Bus: stm32.USART6,
|
||||||
TxAltFuncSelector: AF8_USART4_5_6,
|
TxAltFuncSelector: AF8_USART4_5_6,
|
||||||
RxAltFuncSelector: AF8_USART4_5_6,
|
RxAltFuncSelector: AF8_USART4_5_6,
|
||||||
}
|
}
|
||||||
UART3 = UART{
|
UART3 = &_UART3
|
||||||
|
_UART3 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART1,
|
Bus: stm32.USART1,
|
||||||
TxAltFuncSelector: AF7_USART1_2_3,
|
TxAltFuncSelector: AF7_USART1_2_3,
|
||||||
RxAltFuncSelector: AF7_USART1_2_3,
|
RxAltFuncSelector: AF7_USART1_2_3,
|
||||||
}
|
}
|
||||||
UART0 = UART1
|
DefaultUART = UART1
|
||||||
)
|
)
|
||||||
|
|
||||||
func initUART() {
|
func initUART() {
|
||||||
UART1.Interrupt = interrupt.New(stm32.IRQ_USART3, UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(stm32.IRQ_USART3, _UART1.handleInterrupt)
|
||||||
UART2.Interrupt = interrupt.New(stm32.IRQ_USART6, UART2.handleInterrupt)
|
UART2.Interrupt = interrupt.New(stm32.IRQ_USART6, _UART2.handleInterrupt)
|
||||||
UART3.Interrupt = interrupt.New(stm32.IRQ_USART1, UART3.handleInterrupt)
|
UART3.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART3.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- SPI ----------------------------------------------------------------------
|
// -- SPI ----------------------------------------------------------------------
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// +build feather_rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = GPIO13
|
||||||
|
|
||||||
|
// Onboard crystal oscillator frequency, in MHz.
|
||||||
|
xoscFreq = 12 // MHz
|
||||||
|
)
|
||||||
@@ -139,6 +139,7 @@ const (
|
|||||||
LED_RX = UART_RX_LED_PIN
|
LED_RX = UART_RX_LED_PIN
|
||||||
LED_TX = UART_TX_LED_PIN
|
LED_TX = UART_TX_LED_PIN
|
||||||
NEOPIXEL = NEOPIXEL_PIN
|
NEOPIXEL = NEOPIXEL_PIN
|
||||||
|
WS2812 = NEOPIXEL_PIN
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
|
|||||||
@@ -8,30 +8,34 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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_SERCOM4_2, UART2.handleInterrupt)
|
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_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_SERCOM5_2, UART4.handleInterrupt)
|
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, _UART4.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UART on the Grand Central M4
|
// UART on the Grand Central M4
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM0_USART_INT,
|
Bus: sam.SERCOM0_USART_INT,
|
||||||
SERCOM: 0,
|
SERCOM: 0,
|
||||||
}
|
}
|
||||||
UART2 = UART{
|
UART2 = &_UART2
|
||||||
|
_UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM4_USART_INT,
|
Bus: sam.SERCOM4_USART_INT,
|
||||||
SERCOM: 4,
|
SERCOM: 4,
|
||||||
}
|
}
|
||||||
UART3 = UART{
|
UART3 = &_UART3
|
||||||
|
_UART3 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM1_USART_INT,
|
Bus: sam.SERCOM1_USART_INT,
|
||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
}
|
}
|
||||||
UART4 = UART{
|
UART4 = &_UART4
|
||||||
|
_UART4 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM5_USART_INT,
|
Bus: sam.SERCOM5_USART_INT,
|
||||||
SERCOM: 5,
|
SERCOM: 5,
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ const (
|
|||||||
LED_BLUE = P21
|
LED_BLUE = P21
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var DefaultUART = UART0
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TODO: figure out the pin numbers for these.
|
// TODO: figure out the pin numbers for these.
|
||||||
UART_TX_PIN = D1
|
UART_TX_PIN = D1
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const (
|
|||||||
LED = D13
|
LED = D13
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 aka USBCDC pins
|
// USBCDC pins
|
||||||
const (
|
const (
|
||||||
USBCDC_DM_PIN = PA24
|
USBCDC_DM_PIN = PA24
|
||||||
USBCDC_DP_PIN = PA25
|
USBCDC_DP_PIN = PA25
|
||||||
@@ -56,7 +56,8 @@ const (
|
|||||||
|
|
||||||
// UART1 on the ItsyBitsy M0.
|
// UART1 on the ItsyBitsy M0.
|
||||||
var (
|
var (
|
||||||
UART1 = UART{
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM1_USART,
|
Bus: sam.SERCOM1_USART,
|
||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
@@ -64,7 +65,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, _UART1.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user