mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 730da698a3 |
@@ -16,15 +16,14 @@ jobs:
|
|||||||
name: build-macos
|
name: build-macos
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# macos-14: arm64 (oldest supported version as of 18-11-2025)
|
# macos-13: amd64 (oldest supported version as of 18-10-2024)
|
||||||
# macos-15-intel: amd64 (last intel version to be supported by github runners)
|
# macos-14: arm64 (oldest arm64 version)
|
||||||
# See https://github.com/actions/runner-images/issues/13046
|
os: [macos-13, macos-14]
|
||||||
os: [macos-14, macos-15-intel]
|
|
||||||
include:
|
include:
|
||||||
|
- os: macos-13
|
||||||
|
goarch: amd64
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
- os: macos-15-intel
|
|
||||||
goarch: amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
@@ -40,7 +39,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.1'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
@@ -135,7 +134,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.1'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||||
run: go install -tags=llvm${{ matrix.version }}
|
run: go install -tags=llvm${{ matrix.version }}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
sudo rm -rf /usr/local/share/boost
|
sudo rm -rf /usr/local/share/boost
|
||||||
df -h
|
df -h
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
@@ -66,3 +66,45 @@ jobs:
|
|||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
- name: Trigger Drivers repo build on Github Actions
|
||||||
|
run: |
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
|
||||||
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger Bluetooth repo build on Github Actions
|
||||||
|
run: |
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
|
||||||
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger TinyFS repo build on Github Actions
|
||||||
|
run: |
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \
|
||||||
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger TinyFont repo build on Github Actions
|
||||||
|
run: |
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \
|
||||||
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger TinyDraw repo build on Github Actions
|
||||||
|
run: |
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/tinygo-org/tinydraw/actions/workflows/build.yml/dispatches \
|
||||||
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger TinyTerm repo build on Github Actions
|
||||||
|
run: |
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/tinygo-org/tinyterm/actions/workflows/build.yml/dispatches \
|
||||||
|
-d '{"ref": "dev"}'
|
||||||
|
|||||||
+10
-10
@@ -31,7 +31,7 @@ jobs:
|
|||||||
# We're not on a multi-user machine, so this is safe.
|
# We're not on a multi-user machine, so this is safe.
|
||||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
@@ -131,13 +131,13 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||||
@@ -164,7 +164,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
@@ -179,9 +179,9 @@ jobs:
|
|||||||
simavr \
|
simavr \
|
||||||
ninja-build
|
ninja-build
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@@ -284,7 +284,7 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Get TinyGo version
|
- name: Get TinyGo version
|
||||||
id: version
|
id: version
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
@@ -296,9 +296,9 @@ jobs:
|
|||||||
g++-${{ matrix.toolchain }} \
|
g++-${{ matrix.toolchain }} \
|
||||||
libc6-dev-${{ matrix.libc }}-cross
|
libc6-dev-${{ matrix.libc }}-cross
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
target: tinygo-llvm-build
|
target: tinygo-llvm-build
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||||
run: sudo apt-get remove llvm-18
|
run: sudo apt-get remove llvm-18
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Pull musl, bdwgc
|
- name: Pull musl, bdwgc
|
||||||
run: |
|
run: |
|
||||||
git submodule update --init lib/musl lib/bdwgc
|
git submodule update --init lib/musl lib/bdwgc
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@v22
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
|
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install ninja binaryen
|
scoop install ninja binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
@@ -39,9 +39,9 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore cached LLVM source
|
- name: Restore cached LLVM source
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
@@ -143,11 +143,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install binaryen
|
scoop install binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -173,11 +173,11 @@ jobs:
|
|||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
disk-root: "C:"
|
disk-root: "C:"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -209,11 +209,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install binaryen && scoop install wasmtime@29.0.1
|
scoop install binaryen && scoop install wasmtime@29.0.1
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.7'
|
go-version: '1.25.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
url = https://github.com/WebAssembly/wasi-libc
|
url = https://github.com/WebAssembly/wasi-libc
|
||||||
[submodule "lib/picolibc"]
|
[submodule "lib/picolibc"]
|
||||||
path = lib/picolibc
|
path = lib/picolibc
|
||||||
url = https://github.com/picolibc/picolibc.git
|
url = https://github.com/keith-packard/picolibc.git
|
||||||
[submodule "lib/stm32-svd"]
|
[submodule "lib/stm32-svd"]
|
||||||
path = lib/stm32-svd
|
path = lib/stm32-svd
|
||||||
url = https://github.com/tinygo-org/stm32-svd
|
url = https://github.com/tinygo-org/stm32-svd
|
||||||
|
|||||||
-101
@@ -1,104 +1,3 @@
|
|||||||
0.40.1
|
|
||||||
---
|
|
||||||
* **machine**
|
|
||||||
- nrf: fix flash writes when SoftDevice is enabled
|
|
||||||
* **runtime**
|
|
||||||
- runtime: avoid fixed math/rand sequence on RP2040/RP2350 (#5124)
|
|
||||||
- runtime: add calls to initRand() during run() for all schedulers
|
|
||||||
- runtime: call initRand() before initHeap() during initialization
|
|
||||||
- runtime: use rand_hwrng hardwareRand for RP2040/RP2350 (#5135)
|
|
||||||
* **libs**
|
|
||||||
- picolibc: use updated location for git repo
|
|
||||||
|
|
||||||
0.40.0
|
|
||||||
---
|
|
||||||
* **general**
|
|
||||||
- all: add full LLVM 20 support
|
|
||||||
- core: feat: enable //go:linkname pragma for globals
|
|
||||||
- core: feature: Add flag to ignore go compatibility matrix (#5078)
|
|
||||||
- chore: update version for 0.40 development cycle
|
|
||||||
* **compiler**
|
|
||||||
- emit an error when the actual arch doesn't match GOARCH
|
|
||||||
- mark string parameters as readonly
|
|
||||||
- use Tarjan's SCC algorithm to detect loops for defer
|
|
||||||
- lower "large stack" limit to 16kb
|
|
||||||
* **core**
|
|
||||||
- shrink bdwgc library
|
|
||||||
- Fix linker errors for runtime.vgetrandom and crypto/internal/sysrand.fatal
|
|
||||||
- fix: add TryLock to sync.RWMutex
|
|
||||||
- fix: correct linter issues exposed by the fix in #4679
|
|
||||||
- fix: don't hardcode success return state
|
|
||||||
- fix: expand RTT debugger compatibility
|
|
||||||
- internal/task (threads): save stack bounds instead of scanning under a lock
|
|
||||||
- internal/task: create detached threads and fix error handling
|
|
||||||
- interp: better errors when debugging interp
|
|
||||||
- transform (gc): create stack slots in callers of external functions
|
|
||||||
- internal/task: prevent semaphore resource leak for threads scheduler
|
|
||||||
* **machine**
|
|
||||||
- cortexm: optimize code size for the HardFault_Handler
|
|
||||||
- fe310: add I2C pins for the HiFive1b
|
|
||||||
- clarify WriteAt semantics of BlockDevice
|
|
||||||
- fix deprecated AsmFull comment (#5005)
|
|
||||||
- make sure DMA buffers do not escape unnecessarily
|
|
||||||
- only enable USB-CDC when needed
|
|
||||||
- use larger SPI MAXCNT on nrf52833 and nrf52840
|
|
||||||
- fix: update m.queuedBytes when clamping output to avoid corrupting sentBytes
|
|
||||||
- fix: use int64 in ReadTemperature to avoid overflow
|
|
||||||
- fix(rp2): disable DBGPAUSE on startup
|
|
||||||
- fix(rp2): possible integer overflow while computing factors for SPI baudrate
|
|
||||||
- fix(rp2): reset spinlocks at startup
|
|
||||||
- fix(rp2): switch spinlock busy loop to wfe
|
|
||||||
- fix(rp2): use side-effect-free spinlocks
|
|
||||||
- nrf: add ADC_VDDH which is an ADC pin for VDDH
|
|
||||||
- nrf: don't block SPI transfer
|
|
||||||
- nrf: don't set PSELN, it's ignored in single ended mode anyway
|
|
||||||
- nrf: fix typo in ADC configuration
|
|
||||||
- nrf: refactor SoftDevice enabled check
|
|
||||||
- nrf: rename pwmPin to adcPin
|
|
||||||
- nrf: support flash operations while the SoftDevice is enabled
|
|
||||||
- rp2040: allow writing to the UART inside interrupts
|
|
||||||
- machine,nrf528: stop the bus only once on I2C bus error and ensures the first error is returned
|
|
||||||
* **net**
|
|
||||||
- update submodule to latest commits
|
|
||||||
* **runtime**
|
|
||||||
- (avr): fix infinite longjmp loop if stack is aligned to 256 bytes
|
|
||||||
- (gc_blocks.go): clear full size of allocation
|
|
||||||
- (gc_blocks.go): make sweep branchless
|
|
||||||
- (gc_blocks.go): simplify scanning logic
|
|
||||||
- (gc_blocks.go): use a linked stack to scan marked objects
|
|
||||||
- (gc_blocks.go): use best-fit allocation
|
|
||||||
- (gc_boehm.go): fix world already stopped check
|
|
||||||
- (wasm): scan the system stack
|
|
||||||
- fix sleep duration for long sleeps
|
|
||||||
- remove copied code for nrf52840
|
|
||||||
- src/syscall: update src buffer after write
|
|
||||||
- wasm: fix C realloc and optimize it a bit
|
|
||||||
* **targets**
|
|
||||||
- add xiao-esp32s3 board target
|
|
||||||
- Add ESP32-S3 support (#5091)
|
|
||||||
- Added Gopher ARCADE board
|
|
||||||
- Create "pico2-ice" target board (#5062)
|
|
||||||
* **build/test**
|
|
||||||
- Add testing.T.Context() and testing.B.Context()
|
|
||||||
- create separate go.mod file for testing dependencies for wasm tests that use Chromium headless browser to avoid use of older incompatible version.
|
|
||||||
- go back to normal scheduler instead of tasks scheduler for macos CI
|
|
||||||
- update CI to use Go 1.25.5
|
|
||||||
- update macOS GH actions builds to handle sunset of macOS 13
|
|
||||||
- use task scheduler on macOS builds to avoid test race condition lockups
|
|
||||||
- update all CI builds to use latest stable Go release. Also update some of the actions to their latest releases.
|
|
||||||
- update GH actions builds to use Go 1.25.4
|
|
||||||
- uninstall cmake before install
|
|
||||||
- fix: point the submodule for musl-lib to a mirror in the TinyGo GitHub org
|
|
||||||
- fix: remove macOS 15 from CI build matrix (conflicts with macOS 14 build)
|
|
||||||
- fix: separate host expected bytes from device intended bytes
|
|
||||||
- fix/typo: makeESPFirmwareImage
|
|
||||||
- make: GNUmakefile: shrink TinyGo binaries on Linux
|
|
||||||
- move the directory list into a variable
|
|
||||||
- several improvements to the macOS GH actions build
|
|
||||||
- Fix for #4678: top-level 'make lint' wasn't working
|
|
||||||
- fix: increase the timeout for chromedp to connect to the headless browser used for running the wasm tests.
|
|
||||||
- testdata: some more packages for the test corpus
|
|
||||||
|
|
||||||
0.39.0
|
0.39.0
|
||||||
---
|
---
|
||||||
* **general**
|
* **general**
|
||||||
|
|||||||
+1
-16
@@ -481,7 +481,6 @@ TEST_IOFS := false
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
||||||
TEST_ADDITIONAL_FLAGS ?=
|
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||||
@@ -489,7 +488,7 @@ TEST_ADDITIONAL_FLAGS ?=
|
|||||||
tinygo-test:
|
tinygo-test:
|
||||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||||
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
|
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
|
||||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||||
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
||||||
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||||
@# For more details, see the comments on issue #3143.
|
@# For more details, see the comments on issue #3143.
|
||||||
@@ -622,8 +621,6 @@ smoketest: testchdir
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
# test simulated boards on play.tinygo.org
|
# test simulated boards on play.tinygo.org
|
||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||||
@@ -814,8 +811,6 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny examples/echo
|
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=vicharak_shrike-lite examples/echo
|
|
||||||
@$(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
|
||||||
@@ -898,10 +893,6 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/pwm
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/mcp3008
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(XTENSA), 0)
|
ifneq ($(XTENSA), 0)
|
||||||
@@ -921,12 +912,6 @@ ifneq ($(XTENSA), 0)
|
|||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
|
|
||||||
@$(MD5SUM) test.bin
|
|
||||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/mcp3008
|
|
||||||
@$(MD5SUM) test.bin
|
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-wroom1 examples/mcp3008
|
|
||||||
@$(MD5SUM) test.bin
|
|
||||||
endif
|
endif
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
|
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
Copyright (c) 2018-2026 The TinyGo Authors. All rights reserved.
|
Copyright (c) 2018-2025 The TinyGo Authors. All rights reserved.
|
||||||
|
|
||||||
TinyGo includes portions of the Go standard library.
|
TinyGo includes portions of the Go standard library.
|
||||||
Copyright 2009 The Go Authors. All rights reserved.
|
Copyright (c) 2009-2024 The Go Authors. All rights reserved.
|
||||||
See https://github.com/golang/go/blob/master/LICENSE for license information.
|
|
||||||
|
|
||||||
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
||||||
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ TinyGo is a Go compiler intended for use in small places such as microcontroller
|
|||||||
|
|
||||||
It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language.
|
It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> You can help TinyGo with a financial contribution using OpenCollective. Please see https://opencollective.com/tinygo for more information. Thank you!
|
|
||||||
|
|
||||||
## Embedded
|
## Embedded
|
||||||
|
|
||||||
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
||||||
@@ -66,7 +63,7 @@ tinygo build -buildmode=c-shared -o add.wasm -target=wasip1 add.go
|
|||||||
You can also use the same syntax as Go 1.24+:
|
You can also use the same syntax as Go 1.24+:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
GOOS=wasip1 GOARCH=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
|
GOARCH=wasip1 GOOS=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|||||||
+3
-6
@@ -30,10 +30,7 @@ var BoehmGC = Library{
|
|||||||
// Use a minimal environment.
|
// Use a minimal environment.
|
||||||
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
||||||
"-DDONT_USE_ATEXIT",
|
"-DDONT_USE_ATEXIT",
|
||||||
"-DNO_GETENV", // smaller binary, more predictable configuration
|
"-DNO_GETENV",
|
||||||
"-DNO_CLOCK", // don't use system clock
|
|
||||||
"-DNO_DEBUGGING", // reduce code size
|
|
||||||
"-DGC_NO_FINALIZATION", // finalization is not used at the moment
|
|
||||||
|
|
||||||
// Special flag to work around the lack of __data_start in ld.lld.
|
// Special flag to work around the lack of __data_start in ld.lld.
|
||||||
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
||||||
@@ -42,8 +39,6 @@ var BoehmGC = Library{
|
|||||||
|
|
||||||
// Do not scan the stack. We have our own mechanism to do this.
|
// Do not scan the stack. We have our own mechanism to do this.
|
||||||
"-DSTACK_NOT_SCANNED",
|
"-DSTACK_NOT_SCANNED",
|
||||||
"-DNO_PROC_STAT", // we scan the stack manually (don't read /proc/self/stat on Linux)
|
|
||||||
"-DSTACKBOTTOM=0", // dummy value, we scan the stack manually
|
|
||||||
|
|
||||||
// Assertions can be enabled while debugging GC issues.
|
// Assertions can be enabled while debugging GC issues.
|
||||||
//"-DGC_ASSERTIONS",
|
//"-DGC_ASSERTIONS",
|
||||||
@@ -68,6 +63,7 @@ var BoehmGC = Library{
|
|||||||
"blacklst.c",
|
"blacklst.c",
|
||||||
"dbg_mlc.c",
|
"dbg_mlc.c",
|
||||||
"dyn_load.c",
|
"dyn_load.c",
|
||||||
|
"finalize.c",
|
||||||
"headers.c",
|
"headers.c",
|
||||||
"mach_dep.c",
|
"mach_dep.c",
|
||||||
"malloc.c",
|
"malloc.c",
|
||||||
@@ -75,6 +71,7 @@ var BoehmGC = Library{
|
|||||||
"mark_rts.c",
|
"mark_rts.c",
|
||||||
"misc.c",
|
"misc.c",
|
||||||
"new_hblk.c",
|
"new_hblk.c",
|
||||||
|
"obj_map.c",
|
||||||
"os_dep.c",
|
"os_dep.c",
|
||||||
"reclaim.c",
|
"reclaim.c",
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-10
@@ -19,7 +19,6 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -282,13 +281,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
allFiles[file.Name] = append(allFiles[file.Name], file)
|
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sort embedded files by name to maintain output determinism.
|
for name, files := range allFiles {
|
||||||
embedNames := make([]string, 0, len(allFiles))
|
name := name
|
||||||
for _, files := range allFiles {
|
files := files
|
||||||
embedNames = append(embedNames, files[0].Name)
|
|
||||||
}
|
|
||||||
slices.Sort(embedNames)
|
|
||||||
for _, name := range embedNames {
|
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "make object file for " + name,
|
description: "make object file for " + name,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
@@ -303,7 +298,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
sum := sha256.Sum256(data)
|
sum := sha256.Sum256(data)
|
||||||
hexSum := hex.EncodeToString(sum[:16])
|
hexSum := hex.EncodeToString(sum[:16])
|
||||||
|
|
||||||
for _, file := range allFiles[name] {
|
for _, file := range files {
|
||||||
file.Size = uint64(len(data))
|
file.Size = uint64(len(data))
|
||||||
file.Hash = hexSum
|
file.Hash = hexSum
|
||||||
if file.NeedsData {
|
if file.NeedsData {
|
||||||
@@ -1047,7 +1042,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp8266":
|
case "esp32", "esp32-img", "esp32c3", "esp8266":
|
||||||
// Special format for the ESP family of chips (parsed by the ROM
|
// Special format for the ESP family of chips (parsed by the ROM
|
||||||
// bootloader).
|
// bootloader).
|
||||||
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
"cortex-m4",
|
"cortex-m4",
|
||||||
"cortex-m7",
|
"cortex-m7",
|
||||||
"esp32c3",
|
"esp32c3",
|
||||||
"esp32s3",
|
|
||||||
"fe310",
|
"fe310",
|
||||||
"gameboy-advance",
|
"gameboy-advance",
|
||||||
"k210",
|
"k210",
|
||||||
|
|||||||
+4
-7
@@ -33,13 +33,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||||
if options.GoCompatibility {
|
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
||||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
|
||||||
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the Go toolchain version isn't too new, if we haven't been
|
// Check that the Go toolchain version isn't too new, if we haven't been
|
||||||
|
|||||||
+1
-2
@@ -100,12 +100,11 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
|||||||
chip_id := map[string]uint16{
|
chip_id := map[string]uint16{
|
||||||
"esp32": 0x0000,
|
"esp32": 0x0000,
|
||||||
"esp32c3": 0x0005,
|
"esp32c3": 0x0005,
|
||||||
"esp32s3": 0x0009,
|
|
||||||
}[chip]
|
}[chip]
|
||||||
|
|
||||||
// Image header.
|
// Image header.
|
||||||
switch chip {
|
switch chip {
|
||||||
case "esp32", "esp32c3", "esp32s3":
|
case "esp32", "esp32c3":
|
||||||
// Header format:
|
// Header format:
|
||||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||||
// Note: not adding a SHA256 hash as the binary is modified by
|
// Note: not adding a SHA256 hash as the binary is modified by
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
|
|||||||
// This is a small number of very diverse targets that we want to test.
|
// This is a small number of very diverse targets that we want to test.
|
||||||
tests := []sizeTest{
|
tests := []sizeTest{
|
||||||
// microcontrollers
|
// microcontrollers
|
||||||
{"hifive1b", "examples/echo", 3668, 280, 0, 2244},
|
{"hifive1b", "examples/echo", 3884, 280, 0, 2268},
|
||||||
{"microbit", "examples/serial", 2694, 342, 8, 2248},
|
{"microbit", "examples/serial", 2924, 388, 8, 2272},
|
||||||
{"wioterminal", "examples/pininterrupt", 6837, 1491, 120, 6888},
|
{"wioterminal", "examples/pininterrupt", 7365, 1491, 116, 6912},
|
||||||
|
|
||||||
// TODO: also check wasm. Right now this is difficult, because
|
// TODO: also check wasm. Right now this is difficult, because
|
||||||
// wasm binaries are run through wasm-opt and therefore the
|
// wasm binaries are run through wasm-opt and therefore the
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ func (c *Config) StackSize() uint64 {
|
|||||||
|
|
||||||
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
||||||
func (c *Config) MaxStackAlloc() uint64 {
|
func (c *Config) MaxStackAlloc() uint64 {
|
||||||
if c.StackSize() >= 16*1024 {
|
if c.StackSize() > 32*1024 {
|
||||||
return 1024
|
return 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ type Options struct {
|
|||||||
WITPackage string // pass through to wasm-tools component embed invocation
|
WITPackage string // pass through to wasm-tools component embed invocation
|
||||||
WITWorld string // pass through to wasm-tools component embed -w option
|
WITWorld string // pass through to wasm-tools component embed -w option
|
||||||
ExtLDFlags []string
|
ExtLDFlags []string
|
||||||
GoCompatibility bool // enable to check for Go version compatibility
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
|
|||||||
+1
-1
@@ -245,7 +245,7 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
|||||||
// current insert position.
|
// current insert position.
|
||||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||||
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
|
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||||
|
|
||||||
// Now branch to the out-of-bounds or the regular block.
|
// Now branch to the out-of-bounds or the regular block.
|
||||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ const (
|
|||||||
// Whether this is a full or partial Go parameter (int, slice, etc).
|
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||||
// The extra context parameter is not a Go parameter.
|
// The extra context parameter is not a Go parameter.
|
||||||
paramIsGoParam = 1 << iota
|
paramIsGoParam = 1 << iota
|
||||||
|
|
||||||
// Whether this is a readonly parameter (for example, a string pointer).
|
|
||||||
paramIsReadonly
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||||
@@ -170,7 +167,6 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
suffix := strconv.Itoa(i)
|
suffix := strconv.Itoa(i)
|
||||||
isString := false
|
|
||||||
if goType != nil {
|
if goType != nil {
|
||||||
// Try to come up with a good suffix for this struct field,
|
// Try to come up with a good suffix for this struct field,
|
||||||
// depending on which Go type it's based on.
|
// depending on which Go type it's based on.
|
||||||
@@ -187,16 +183,12 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
|||||||
suffix = []string{"r", "i"}[i]
|
suffix = []string{"r", "i"}[i]
|
||||||
case types.String:
|
case types.String:
|
||||||
suffix = []string{"data", "len"}[i]
|
suffix = []string{"data", "len"}[i]
|
||||||
isString = true
|
|
||||||
}
|
}
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
suffix = []string{"context", "funcptr"}[i]
|
suffix = []string{"context", "funcptr"}[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||||
if isString {
|
|
||||||
subInfos[0].flags |= paramIsReadonly
|
|
||||||
}
|
|
||||||
paramInfos = append(paramInfos, subInfos...)
|
paramInfos = append(paramInfos, subInfos...)
|
||||||
}
|
}
|
||||||
return paramInfos
|
return paramInfos
|
||||||
|
|||||||
+28
-132
@@ -152,12 +152,10 @@ type builder struct {
|
|||||||
llvmFnType llvm.Type
|
llvmFnType llvm.Type
|
||||||
llvmFn llvm.Value
|
llvmFn llvm.Value
|
||||||
info functionInfo
|
info functionInfo
|
||||||
locals map[ssa.Value]llvm.Value // local variables
|
locals map[ssa.Value]llvm.Value // local variables
|
||||||
blockInfo []blockInfo
|
blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
|
||||||
|
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
|
||||||
currentBlock *ssa.BasicBlock
|
currentBlock *ssa.BasicBlock
|
||||||
currentBlockInfo *blockInfo
|
|
||||||
tarjanStack []uint
|
|
||||||
tarjanIndex uint
|
|
||||||
phis []phiNode
|
phis []phiNode
|
||||||
deferPtr llvm.Value
|
deferPtr llvm.Value
|
||||||
deferFrame llvm.Value
|
deferFrame llvm.Value
|
||||||
@@ -189,22 +187,11 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
|
|||||||
info: c.getFunctionInfo(f),
|
info: c.getFunctionInfo(f),
|
||||||
locals: make(map[ssa.Value]llvm.Value),
|
locals: make(map[ssa.Value]llvm.Value),
|
||||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||||
|
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||||
|
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type blockInfo struct {
|
|
||||||
// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
|
|
||||||
entry llvm.BasicBlock
|
|
||||||
|
|
||||||
// exit is the LLVM basic block corresponding to the end of this *ssa.Block.
|
|
||||||
// It will be different than entry if any of the block's instructions contain internal branches.
|
|
||||||
exit llvm.BasicBlock
|
|
||||||
|
|
||||||
// tarjan holds state for applying Tarjan's strongly connected components algorithm to the CFG.
|
|
||||||
// This is used by defer.go to determine whether to stack- or heap-allocate defer data.
|
|
||||||
tarjan tarjanNode
|
|
||||||
}
|
|
||||||
|
|
||||||
type deferBuiltin struct {
|
type deferBuiltin struct {
|
||||||
callName string
|
callName string
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
@@ -1233,29 +1220,14 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
|||||||
// intrinsic (like an atomic operation). Create the entry block
|
// intrinsic (like an atomic operation). Create the entry block
|
||||||
// manually.
|
// manually.
|
||||||
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||||
// Intrinsics may create internal branches (e.g. nil checks).
|
|
||||||
// They will attempt to access b.currentBlockInfo to update the exit block.
|
|
||||||
// Create some fake block info for them to access.
|
|
||||||
blockInfo := []blockInfo{
|
|
||||||
{
|
|
||||||
entry: entryBlock,
|
|
||||||
exit: entryBlock,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b.blockInfo = blockInfo
|
|
||||||
b.currentBlockInfo = &blockInfo[0]
|
|
||||||
} else {
|
} else {
|
||||||
blocks := b.fn.Blocks
|
|
||||||
blockInfo := make([]blockInfo, len(blocks))
|
|
||||||
for _, block := range b.fn.DomPreorder() {
|
for _, block := range b.fn.DomPreorder() {
|
||||||
info := &blockInfo[block.Index]
|
|
||||||
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
|
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
|
||||||
info.entry = llvmBlock
|
b.blockEntries[block] = llvmBlock
|
||||||
info.exit = llvmBlock
|
b.blockExits[block] = llvmBlock
|
||||||
}
|
}
|
||||||
b.blockInfo = blockInfo
|
|
||||||
// Normal functions have an entry block.
|
// Normal functions have an entry block.
|
||||||
entryBlock = blockInfo[0].entry
|
entryBlock = b.blockEntries[b.fn.Blocks[0]]
|
||||||
}
|
}
|
||||||
b.SetInsertPointAtEnd(entryBlock)
|
b.SetInsertPointAtEnd(entryBlock)
|
||||||
|
|
||||||
@@ -1351,9 +1323,8 @@ func (b *builder) createFunction() {
|
|||||||
if b.DumpSSA {
|
if b.DumpSSA {
|
||||||
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
||||||
}
|
}
|
||||||
|
b.SetInsertPointAtEnd(b.blockEntries[block])
|
||||||
b.currentBlock = block
|
b.currentBlock = block
|
||||||
b.currentBlockInfo = &b.blockInfo[block.Index]
|
|
||||||
b.SetInsertPointAtEnd(b.currentBlockInfo.entry)
|
|
||||||
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 {
|
if !b.Debug {
|
||||||
@@ -1413,7 +1384,7 @@ func (b *builder) createFunction() {
|
|||||||
block := phi.ssa.Block()
|
block := phi.ssa.Block()
|
||||||
for i, edge := range phi.ssa.Edges {
|
for i, edge := range phi.ssa.Edges {
|
||||||
llvmVal := b.getValue(edge, getPos(phi.ssa))
|
llvmVal := b.getValue(edge, getPos(phi.ssa))
|
||||||
llvmBlock := b.blockInfo[block.Preds[i].Index].exit
|
llvmBlock := b.blockExits[block.Preds[i]]
|
||||||
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
|
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1527,11 +1498,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||||||
case *ssa.If:
|
case *ssa.If:
|
||||||
cond := b.getValue(instr.Cond, getPos(instr))
|
cond := b.getValue(instr.Cond, getPos(instr))
|
||||||
block := instr.Block()
|
block := instr.Block()
|
||||||
blockThen := b.blockInfo[block.Succs[0].Index].entry
|
blockThen := b.blockEntries[block.Succs[0]]
|
||||||
blockElse := b.blockInfo[block.Succs[1].Index].entry
|
blockElse := b.blockEntries[block.Succs[1]]
|
||||||
b.CreateCondBr(cond, blockThen, blockElse)
|
b.CreateCondBr(cond, blockThen, blockElse)
|
||||||
case *ssa.Jump:
|
case *ssa.Jump:
|
||||||
blockJump := b.blockInfo[instr.Block().Succs[0].Index].entry
|
blockJump := b.blockEntries[instr.Block().Succs[0]]
|
||||||
b.CreateBr(blockJump)
|
b.CreateBr(blockJump)
|
||||||
case *ssa.MapUpdate:
|
case *ssa.MapUpdate:
|
||||||
m := b.getValue(instr.Map, getPos(instr))
|
m := b.getValue(instr.Map, getPos(instr))
|
||||||
@@ -1599,8 +1570,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
||||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
elemLayout := b.createObjectLayout(elemType, pos)
|
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
|
||||||
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize, elemLayout}, "append.new")
|
|
||||||
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
||||||
newLen := b.CreateExtractValue(result, 1, "append.newLen")
|
newLen := b.CreateExtractValue(result, 1, "append.newLen")
|
||||||
newCap := b.CreateExtractValue(result, 2, "append.newCap")
|
newCap := b.CreateExtractValue(result, 2, "append.newCap")
|
||||||
@@ -1682,41 +1652,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
case "copy":
|
case "copy":
|
||||||
dst := argValues[0]
|
dst := argValues[0]
|
||||||
src := argValues[1]
|
src := argValues[1]
|
||||||
// Fetch the lengths.
|
|
||||||
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
||||||
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
||||||
// Find the minimum of the lengths.
|
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
||||||
minFuncName := "llvm.umin.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
||||||
minFunc := b.mod.NamedFunction(minFuncName)
|
|
||||||
if minFunc.IsNil() {
|
|
||||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType, b.uintptrType}, false)
|
|
||||||
minFunc = llvm.AddFunction(b.mod, minFuncName, fnType)
|
|
||||||
}
|
|
||||||
minLen := b.CreateCall(minFunc.GlobalValueType(), minFunc, []llvm.Value{dstLen, srcLen}, "copy.n")
|
|
||||||
// Multiply the length by the element size.
|
|
||||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
// NOTE: This is also NSW when uintptr is int, but we can only choose one through the C API?
|
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
||||||
size := b.CreateNUWMul(minLen, elemSize, "copy.size")
|
|
||||||
// Fetch the pointers.
|
|
||||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstPtr")
|
|
||||||
srcBuf := b.CreateExtractValue(src, 0, "copy.srcPtr")
|
|
||||||
// Create a memcpy.
|
|
||||||
call := b.createMemCopy("memmove", dstBuf, srcBuf, size)
|
|
||||||
align := b.targetData.ABITypeAlignment(elemType)
|
|
||||||
if align > 1 {
|
|
||||||
// Apply the type's alignment to the arguments.
|
|
||||||
// LLVM sometimes turns constant-length moves into loads and stores.
|
|
||||||
// It may use this alignment for the created loads and stores.
|
|
||||||
alignAttr := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align))
|
|
||||||
call.AddCallSiteAttribute(1, alignAttr)
|
|
||||||
call.AddCallSiteAttribute(2, alignAttr)
|
|
||||||
}
|
|
||||||
// Extend and return the copied length.
|
|
||||||
if b.targetData.TypeAllocSize(minLen.Type()) < b.targetData.TypeAllocSize(b.intType) {
|
|
||||||
minLen = b.CreateZExt(minLen, b.intType, "copy.n.zext")
|
|
||||||
}
|
|
||||||
return minLen, nil
|
|
||||||
case "delete":
|
case "delete":
|
||||||
m := argValues[0]
|
m := argValues[0]
|
||||||
key := argValues[1]
|
key := argValues[1]
|
||||||
@@ -1744,66 +1686,20 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
return llvmLen, nil
|
return llvmLen, nil
|
||||||
case "min", "max":
|
case "min", "max":
|
||||||
// min and max builtins, added in Go 1.21.
|
// min and max builtins, added in Go 1.21.
|
||||||
// Find the corresponding intrinsic name.
|
// We can simply reuse the existing binop comparison code, which has all
|
||||||
ty := argTypes[0].Underlying().(*types.Basic)
|
// the edge cases figured out already.
|
||||||
llvmType := b.getLLVMType(ty)
|
tok := token.LSS
|
||||||
info := ty.Info()
|
if callName == "max" {
|
||||||
var prefix, delimeter, typeName string
|
tok = token.GTR
|
||||||
if info&types.IsInteger != 0 {
|
|
||||||
// This is an integer value.
|
|
||||||
// Use the LLVM int min/max intrinsics.
|
|
||||||
prefix = "llvm.s"
|
|
||||||
if info&types.IsUnsigned != 0 {
|
|
||||||
prefix = "llvm.u"
|
|
||||||
}
|
|
||||||
delimeter = ".i"
|
|
||||||
typeName = strconv.Itoa(llvmType.IntTypeWidth())
|
|
||||||
} else {
|
|
||||||
switch ty.Kind() {
|
|
||||||
case types.String:
|
|
||||||
// Strings do not have an equivalent intrinsic.
|
|
||||||
// Implement with compares and selects.
|
|
||||||
tok := token.LSS
|
|
||||||
if callName == "max" {
|
|
||||||
tok = token.GTR
|
|
||||||
}
|
|
||||||
result := argValues[0]
|
|
||||||
typ := argTypes[0]
|
|
||||||
for _, arg := range argValues[1:] {
|
|
||||||
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
result = b.CreateSelect(cmp, result, arg, "")
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
case types.Float32:
|
|
||||||
typeName = "f32"
|
|
||||||
case types.Float64:
|
|
||||||
typeName = "f64"
|
|
||||||
default:
|
|
||||||
return llvm.Value{}, b.makeError(pos, "todo: min/max: unknown type")
|
|
||||||
}
|
|
||||||
// There are a few edge cases with floating point min/max:
|
|
||||||
// min(-0.0, +0.0) = -0.0
|
|
||||||
// min(NaN, number) = NaN
|
|
||||||
// The llvm.minimum.*/llvm.maximum.* intrinsics match this behavior.
|
|
||||||
// Neither Go nor LLVM defines the bit representation of resulting NaNs.
|
|
||||||
prefix = "llvm."
|
|
||||||
delimeter = "imum."
|
|
||||||
}
|
}
|
||||||
intrinsicName := prefix + callName + delimeter + typeName
|
|
||||||
// Find or create the intrinsic.
|
|
||||||
llvmFn := b.mod.NamedFunction(intrinsicName)
|
|
||||||
if llvmFn.IsNil() {
|
|
||||||
fnType := llvm.FunctionType(llvmType, []llvm.Type{llvmType, llvmType}, false)
|
|
||||||
llvmFn = llvm.AddFunction(b.mod, intrinsicName, fnType)
|
|
||||||
}
|
|
||||||
// Call the intrinsic repeatedly to merge the arguments.
|
|
||||||
callType := llvmFn.GlobalValueType()
|
|
||||||
result := argValues[0]
|
result := argValues[0]
|
||||||
|
typ := argTypes[0]
|
||||||
for _, arg := range argValues[1:] {
|
for _, arg := range argValues[1:] {
|
||||||
result = b.CreateCall(callType, llvmFn, []llvm.Value{result, arg}, "")
|
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result = b.CreateSelect(cmp, result, arg, "")
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
case "panic":
|
case "panic":
|
||||||
|
|||||||
+29
-99
@@ -100,7 +100,7 @@ func (b *builder) createLandingPad() {
|
|||||||
|
|
||||||
// Continue at the 'recover' block, which returns to the parent in an
|
// Continue at the 'recover' block, which returns to the parent in an
|
||||||
// appropriate way.
|
// appropriate way.
|
||||||
b.CreateBr(b.blockInfo[b.fn.Recover.Index].entry)
|
b.CreateBr(b.blockEntries[b.fn.Recover])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a checkpoint (similar to setjmp). This emits inline assembly that
|
// Create a checkpoint (similar to setjmp). This emits inline assembly that
|
||||||
@@ -234,108 +234,41 @@ func (b *builder) createInvokeCheckpoint() {
|
|||||||
continueBB := b.insertBasicBlock("")
|
continueBB := b.insertBasicBlock("")
|
||||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||||
b.SetInsertPointAtEnd(continueBB)
|
b.SetInsertPointAtEnd(continueBB)
|
||||||
b.currentBlockInfo.exit = continueBB
|
b.blockExits[b.currentBlock] = continueBB
|
||||||
}
|
}
|
||||||
|
|
||||||
// isInLoop checks if there is a path from the current block to itself.
|
// isInLoop checks if there is a path from a basic block to itself.
|
||||||
// Use Tarjan's strongly connected components algorithm to search for cycles.
|
func isInLoop(start *ssa.BasicBlock) bool {
|
||||||
// A one-node SCC is a cycle iff there is an edge from the node to itself.
|
// Use a breadth-first search to scan backwards through the block graph.
|
||||||
// A multi-node SCC is always a cycle.
|
queue := []*ssa.BasicBlock{start}
|
||||||
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
checked := map[*ssa.BasicBlock]struct{}{}
|
||||||
func (b *builder) isInLoop() bool {
|
|
||||||
if b.currentBlockInfo.tarjan.lowLink == 0 {
|
|
||||||
b.strongConnect(b.currentBlock)
|
|
||||||
}
|
|
||||||
return b.currentBlockInfo.tarjan.cyclic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *builder) strongConnect(block *ssa.BasicBlock) {
|
for len(queue) > 0 {
|
||||||
// Assign a new index.
|
// pop a block off of the queue
|
||||||
// Indices start from 1 so that 0 can be used as a sentinel.
|
block := queue[len(queue)-1]
|
||||||
assignedIndex := b.tarjanIndex + 1
|
queue = queue[:len(queue)-1]
|
||||||
b.tarjanIndex = assignedIndex
|
|
||||||
|
|
||||||
// Apply the new index.
|
// Search through predecessors.
|
||||||
blockIndex := block.Index
|
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
|
||||||
node := &b.blockInfo[blockIndex].tarjan
|
// Defers are often placed near the start of the function.
|
||||||
node.lowLink = assignedIndex
|
for _, pred := range block.Preds {
|
||||||
|
if pred == start {
|
||||||
// Push the node onto the stack.
|
// cycle found
|
||||||
node.onStack = true
|
return true
|
||||||
b.tarjanStack = append(b.tarjanStack, uint(blockIndex))
|
|
||||||
|
|
||||||
// Process the successors.
|
|
||||||
for _, successor := range block.Succs {
|
|
||||||
// Look up the successor's state.
|
|
||||||
successorIndex := successor.Index
|
|
||||||
if successorIndex == blockIndex {
|
|
||||||
// Handle a self-cycle specially.
|
|
||||||
node.cyclic = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
successorNode := &b.blockInfo[successorIndex].tarjan
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case successorNode.lowLink == 0:
|
|
||||||
// This node has not yet been visisted.
|
|
||||||
b.strongConnect(successor)
|
|
||||||
|
|
||||||
case !successorNode.onStack:
|
|
||||||
// This node has been visited, but is in a different SCC.
|
|
||||||
// Ignore it, and do not update lowLink.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the lowLink index.
|
|
||||||
// This always uses the min-of-lowlink instead of using index in the on-stack case.
|
|
||||||
// This is done for two reasons:
|
|
||||||
// 1. The lowLink update can be shared between the new-node and on-stack cases.
|
|
||||||
// 2. The assigned index does not need to be saved - it is only needed for root node detection.
|
|
||||||
if successorNode.lowLink < node.lowLink {
|
|
||||||
node.lowLink = successorNode.lowLink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.lowLink == assignedIndex {
|
|
||||||
// This is a root node.
|
|
||||||
// Pop the SCC off the stack.
|
|
||||||
stack := b.tarjanStack
|
|
||||||
top := stack[len(stack)-1]
|
|
||||||
stack = stack[:len(stack)-1]
|
|
||||||
blocks := b.blockInfo
|
|
||||||
topNode := &blocks[top].tarjan
|
|
||||||
topNode.onStack = false
|
|
||||||
|
|
||||||
if top != uint(blockIndex) {
|
|
||||||
// The root node is not the only node in the SCC.
|
|
||||||
// Mark all nodes in this SCC as cyclic.
|
|
||||||
topNode.cyclic = true
|
|
||||||
for top != uint(blockIndex) {
|
|
||||||
top = stack[len(stack)-1]
|
|
||||||
stack = stack[:len(stack)-1]
|
|
||||||
topNode = &blocks[top].tarjan
|
|
||||||
topNode.onStack = false
|
|
||||||
topNode.cyclic = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := checked[pred]; ok {
|
||||||
|
// block already checked
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to queue and checked map
|
||||||
|
queue = append(queue, pred)
|
||||||
|
checked[pred] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.tarjanStack = stack
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// tarjanNode holds per-block state for isInLoop and strongConnect.
|
return false
|
||||||
type tarjanNode struct {
|
|
||||||
// lowLink is the index of the first visited node that is reachable from this block.
|
|
||||||
// The lowLink indices are assigned by the SCC search, and do not correspond to b.Index.
|
|
||||||
// A lowLink of 0 is used as a sentinel to mark a node which has not yet been visited.
|
|
||||||
lowLink uint
|
|
||||||
|
|
||||||
// onStack tracks whether this node is currently on the SCC search stack.
|
|
||||||
onStack bool
|
|
||||||
|
|
||||||
// cyclic indicates whether this block is in a loop.
|
|
||||||
// If lowLink is 0, strongConnect must be called before reading this field.
|
|
||||||
cyclic bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createDefer emits a single defer instruction, to be run when this function
|
// createDefer emits a single defer instruction, to be run when this function
|
||||||
@@ -477,10 +410,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
|
|
||||||
// Put this struct in an allocation.
|
// Put this struct in an allocation.
|
||||||
var alloca llvm.Value
|
var alloca llvm.Value
|
||||||
if instr.Block() != b.currentBlock {
|
if !isInLoop(instr.Block()) {
|
||||||
panic("block mismatch")
|
|
||||||
}
|
|
||||||
if !b.isInLoop() {
|
|
||||||
// This can safely use a stack allocation.
|
// This can safely use a stack allocation.
|
||||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -99,9 +99,6 @@ func typeHasPointers(t llvm.Type) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
if t.ArrayLength() == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if typeHasPointers(t.ElementType()) {
|
if typeHasPointers(t.ElementType()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
|||||||
// provided immediately. For example:
|
// provided immediately. For example:
|
||||||
//
|
//
|
||||||
// arm.AsmFull(
|
// arm.AsmFull(
|
||||||
// "str {value}, [{result}]",
|
// "str {value}, {result}",
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "value": 1,
|
// "value": 1
|
||||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
// "result": &dest,
|
||||||
// })
|
// })
|
||||||
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||||
|
|||||||
@@ -737,7 +737,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
prevBlock := b.GetInsertBlock()
|
prevBlock := b.GetInsertBlock()
|
||||||
okBlock := b.insertBasicBlock("typeassert.ok")
|
okBlock := b.insertBasicBlock("typeassert.ok")
|
||||||
nextBlock := b.insertBasicBlock("typeassert.next")
|
nextBlock := b.insertBasicBlock("typeassert.next")
|
||||||
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
|
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||||
|
|
||||||
// Retrieve the value from the interface if the type assert was
|
// Retrieve the value from the interface if the type assert was
|
||||||
|
|||||||
+8
-29
@@ -27,8 +27,6 @@ func (b *builder) defineIntrinsicFunction() {
|
|||||||
b.createStackSaveImpl()
|
b.createStackSaveImpl()
|
||||||
case name == "runtime.KeepAlive":
|
case name == "runtime.KeepAlive":
|
||||||
b.createKeepAliveImpl()
|
b.createKeepAliveImpl()
|
||||||
case name == "machine.keepAliveNoEscape":
|
|
||||||
b.createMachineKeepAliveImpl()
|
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||||
b.createVolatileLoad()
|
b.createVolatileLoad()
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
@@ -50,24 +48,19 @@ func (b *builder) defineIntrinsicFunction() {
|
|||||||
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
||||||
func (b *builder) createMemoryCopyImpl() {
|
func (b *builder) createMemoryCopyImpl() {
|
||||||
b.createFunctionStart(true)
|
b.createFunctionStart(true)
|
||||||
params := b.fn.Params[0:3]
|
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
b.createMemCopy(
|
|
||||||
b.fn.Name(),
|
|
||||||
b.getValue(params[0], getPos(b.fn)),
|
|
||||||
b.getValue(params[1], getPos(b.fn)),
|
|
||||||
b.getValue(params[2], getPos(b.fn)),
|
|
||||||
)
|
|
||||||
b.CreateRetVoid()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *builder) createMemCopy(kind string, dst, src, len llvm.Value) llvm.Value {
|
|
||||||
fnName := "llvm." + kind + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
|
||||||
llvmFn := b.mod.NamedFunction(fnName)
|
llvmFn := b.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
return b.CreateCall(llvmFn.GlobalValueType(), llvmFn, []llvm.Value{dst, src, len, llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "")
|
var params []llvm.Value
|
||||||
|
for _, param := range b.fn.Params {
|
||||||
|
params = append(params, b.getValue(param, getPos(b.fn)))
|
||||||
|
}
|
||||||
|
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
||||||
|
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||||
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
||||||
@@ -151,20 +144,6 @@ func (b *builder) createAbiEscapeImpl() {
|
|||||||
b.CreateRet(result)
|
b.CreateRet(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement machine.keepAliveNoEscape, which makes sure the compiler keeps the
|
|
||||||
// pointer parameter alive until this point (for GC).
|
|
||||||
func (b *builder) createMachineKeepAliveImpl() {
|
|
||||||
b.createFunctionStart(true)
|
|
||||||
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
|
|
||||||
|
|
||||||
// See createKeepAliveImpl for details.
|
|
||||||
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
|
|
||||||
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
|
|
||||||
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
|
||||||
|
|
||||||
b.CreateRetVoid()
|
|
||||||
}
|
|
||||||
|
|
||||||
var mathToLLVMMapping = map[string]string{
|
var mathToLLVMMapping = map[string]string{
|
||||||
"math.Ceil": "llvm.ceil.f64",
|
"math.Ceil": "llvm.ceil.f64",
|
||||||
"math.Exp": "llvm.exp.f64",
|
"math.Exp": "llvm.exp.f64",
|
||||||
|
|||||||
+93
-93
@@ -1,10 +1,10 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -231,12 +231,6 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
|||||||
//
|
//
|
||||||
// For details on what's in this value, see src/runtime/gc_precise.go.
|
// For details on what's in this value, see src/runtime/gc_precise.go.
|
||||||
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||||
if !typeHasPointers(t) {
|
|
||||||
// There are no pointers in this type, so we can simplify the layout.
|
|
||||||
layout := (uint64(1) << 1) | 1
|
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the element type for arrays. This works even for nested arrays.
|
// Use the element type for arrays. This works even for nested arrays.
|
||||||
for {
|
for {
|
||||||
kind := t.TypeKind()
|
kind := t.TypeKind()
|
||||||
@@ -254,29 +248,54 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the pointer bitmap.
|
// Do a few checks to see whether we need to generate any object layout
|
||||||
|
// information at all.
|
||||||
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||||
pointerAlignment := uint64(c.targetData.PrefTypeAlignment(c.dataPtrType))
|
|
||||||
bitmapLen := objectSizeBytes / pointerAlignment
|
|
||||||
bitmapBytes := (bitmapLen + 7) / 8
|
|
||||||
bitmap := make([]byte, bitmapBytes, max(bitmapBytes, 8))
|
|
||||||
c.buildPointerBitmap(bitmap, pointerAlignment, pos, t, 0)
|
|
||||||
|
|
||||||
// Try to encode the layout inline.
|
|
||||||
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
||||||
pointerBits := pointerSize * 8
|
pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||||
if bitmapLen < pointerBits {
|
if objectSizeBytes < pointerSize {
|
||||||
rawMask := binary.LittleEndian.Uint64(bitmap[0:8])
|
// Too small to contain a pointer.
|
||||||
layout := rawMask*pointerBits + bitmapLen
|
layout := (uint64(1) << 1) | 1
|
||||||
layout <<= 1
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
layout |= 1
|
}
|
||||||
|
bitmap := c.getPointerBitmap(t, pos)
|
||||||
|
if bitmap.BitLen() == 0 {
|
||||||
|
// There are no pointers in this type, so we can simplify the layout.
|
||||||
|
// TODO: this can be done in many other cases, e.g. when allocating an
|
||||||
|
// array (like [4][]byte, which repeats a slice 4 times).
|
||||||
|
layout := (uint64(1) << 1) | 1
|
||||||
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
|
}
|
||||||
|
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
||||||
|
// This shouldn't happen except for packed structs, which aren't
|
||||||
|
// currently used.
|
||||||
|
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
||||||
|
return llvm.ConstNull(c.dataPtrType)
|
||||||
|
}
|
||||||
|
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
||||||
|
|
||||||
// Check if the layout fits.
|
pointerBits := pointerSize * 8
|
||||||
layout &= 1<<pointerBits - 1
|
var sizeFieldBits uint64
|
||||||
if (layout>>1)/pointerBits == rawMask {
|
switch pointerBits {
|
||||||
// No set bits were shifted off.
|
case 16:
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
sizeFieldBits = 4
|
||||||
}
|
case 32:
|
||||||
|
sizeFieldBits = 5
|
||||||
|
case 64:
|
||||||
|
sizeFieldBits = 6
|
||||||
|
default:
|
||||||
|
panic("unknown pointer size")
|
||||||
|
}
|
||||||
|
layoutFieldBits := pointerBits - 1 - sizeFieldBits
|
||||||
|
|
||||||
|
// Try to emit the value as an inline integer. This is possible in most
|
||||||
|
// cases.
|
||||||
|
if objectSizeWords < layoutFieldBits {
|
||||||
|
// If it can be stored directly in the pointer value, do so.
|
||||||
|
// The runtime knows that if the least significant bit of the pointer is
|
||||||
|
// set, the pointer contains the value itself.
|
||||||
|
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
||||||
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||||
@@ -284,24 +303,25 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
|
|
||||||
// Try first whether the global already exists. All objects with a
|
// Try first whether the global already exists. All objects with a
|
||||||
// particular name have the same type, so this is possible.
|
// particular name have the same type, so this is possible.
|
||||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", bitmapLen, (bitmapLen+15)/16, bitmap)
|
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
||||||
global := c.mod.NamedGlobal(globalName)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if !global.IsNil() {
|
if !global.IsNil() {
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the global initializer.
|
// Create the global initializer.
|
||||||
bitmapByteValues := make([]llvm.Value, bitmapBytes)
|
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
||||||
i8 := c.ctx.Int8Type()
|
bitmap.FillBytes(bitmapBytes)
|
||||||
for i, b := range bitmap {
|
reverseBytes(bitmapBytes) // big-endian to little-endian
|
||||||
bitmapByteValues[i] = llvm.ConstInt(i8, uint64(b), false)
|
var bitmapByteValues []llvm.Value
|
||||||
|
for _, b := range bitmapBytes {
|
||||||
|
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
|
||||||
}
|
}
|
||||||
initializer := c.ctx.ConstStruct([]llvm.Value{
|
initializer := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, bitmapLen, false),
|
llvm.ConstInt(c.uintptrType, objectSizeWords, false),
|
||||||
llvm.ConstArray(i8, bitmapByteValues),
|
llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues),
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
// Create the actual global.
|
|
||||||
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
@@ -309,7 +329,6 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
||||||
// AVR doesn't have alignment by default.
|
// AVR doesn't have alignment by default.
|
||||||
// The lowest bit must be unset to distinguish this from an inline layout.
|
|
||||||
global.SetAlignment(2)
|
global.SetAlignment(2)
|
||||||
}
|
}
|
||||||
if c.Debug && pos != token.NoPos {
|
if c.Debug && pos != token.NoPos {
|
||||||
@@ -341,71 +360,52 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||||
// bitmap at the word offset that contains a pointer. This scan is recursive.
|
// bigint at the word offset that contains a pointer. This scan is recursive.
|
||||||
func (c *compilerContext) buildPointerBitmap(
|
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
||||||
dst []byte,
|
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||||
ptrAlign uint64,
|
switch typ.TypeKind() {
|
||||||
pos token.Pos,
|
|
||||||
t llvm.Type,
|
|
||||||
offset uint64,
|
|
||||||
) {
|
|
||||||
switch t.TypeKind() {
|
|
||||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||||
// These types do not contain pointers.
|
return big.NewInt(0)
|
||||||
|
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
// Set the corresponding position in the bitmap.
|
return big.NewInt(1)
|
||||||
dst[offset/8] |= 1 << (offset % 8)
|
|
||||||
|
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
// Recurse over struct elements.
|
ptrs := big.NewInt(0)
|
||||||
for i, et := range t.StructElementTypes() {
|
for i, subtyp := range typ.StructElementTypes() {
|
||||||
eo := c.targetData.ElementOffset(t, i)
|
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||||
if eo%uint64(ptrAlign) != 0 {
|
if subptrs.BitLen() == 0 {
|
||||||
if typeHasPointers(et) {
|
|
||||||
// This error will let the compilation fail, but by continuing
|
|
||||||
// the error can still easily be shown.
|
|
||||||
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.buildPointerBitmap(
|
offset := c.targetData.ElementOffset(typ, i)
|
||||||
dst,
|
if offset%uint64(alignment) != 0 {
|
||||||
ptrAlign,
|
// This error will let the compilation fail, but by continuing
|
||||||
pos,
|
// the error can still easily be shown.
|
||||||
et,
|
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||||
offset+(eo/ptrAlign),
|
continue
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case llvm.ArrayTypeKind:
|
|
||||||
// Recurse over array elements.
|
|
||||||
len := t.ArrayLength()
|
|
||||||
if len <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
et := t.ElementType()
|
|
||||||
elementSize := c.targetData.TypeAllocSize(et)
|
|
||||||
if elementSize%ptrAlign != 0 {
|
|
||||||
if typeHasPointers(et) {
|
|
||||||
// This error will let the compilation fail (but continues so that
|
|
||||||
// other errors can be shown).
|
|
||||||
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
|
||||||
}
|
}
|
||||||
return
|
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
||||||
|
ptrs.Or(ptrs, subptrs)
|
||||||
}
|
}
|
||||||
elementSize /= ptrAlign
|
return ptrs
|
||||||
for i := 0; i < len; i++ {
|
case llvm.ArrayTypeKind:
|
||||||
c.buildPointerBitmap(
|
subtyp := typ.ElementType()
|
||||||
dst,
|
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||||
ptrAlign,
|
ptrs := big.NewInt(0)
|
||||||
pos,
|
if subptrs.BitLen() == 0 {
|
||||||
et,
|
return ptrs
|
||||||
offset+uint64(i)*elementSize,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
elementSize := c.targetData.TypeAllocSize(subtyp)
|
||||||
|
if elementSize%uint64(alignment) != 0 {
|
||||||
|
// This error will let the compilation fail (but continues so that
|
||||||
|
// other errors can be shown).
|
||||||
|
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||||
|
return ptrs
|
||||||
|
}
|
||||||
|
for i := 0; i < typ.ArrayLength(); i++ {
|
||||||
|
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
||||||
|
ptrs.Or(ptrs, subptrs)
|
||||||
|
}
|
||||||
|
return ptrs
|
||||||
default:
|
default:
|
||||||
// Should not happen.
|
// Should not happen.
|
||||||
panic("unknown LLVM type")
|
panic("unknown LLVM type")
|
||||||
|
|||||||
@@ -250,9 +250,6 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||||
switch keyType := keyType.Underlying().(type) {
|
switch keyType := keyType.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
// TODO: unsafe.Pointer is also a binary key, but to support that we
|
|
||||||
// need to fix an issue with interp first (see
|
|
||||||
// https://github.com/tinygo-org/tinygo/pull/4898).
|
|
||||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
return true
|
return true
|
||||||
|
|||||||
+6
-7
@@ -142,11 +142,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||||
}
|
}
|
||||||
if paramInfo.flags¶mIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
|
|
||||||
// Readonly pointer parameters (like strings) benefit from being marked as readonly.
|
|
||||||
readonly := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)
|
|
||||||
llvmFn.AddAttributeAtIndex(i+1, readonly)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a number of function or parameter attributes, depending on the
|
// Set a number of function or parameter attributes, depending on the
|
||||||
@@ -159,8 +154,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
||||||
case "internal/abi.NoEscape":
|
case "internal/abi.NoEscape":
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
|
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
|
||||||
case "runtime.alloc":
|
case "runtime.alloc":
|
||||||
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
||||||
// returns values that are never null and never alias to an existing value.
|
// returns values that are never null and never alias to an existing value.
|
||||||
@@ -184,6 +177,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// be modified.
|
// be modified.
|
||||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
|
case "runtime.sliceCopy":
|
||||||
|
// Copying a slice won't capture any of the parameters.
|
||||||
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
case "runtime.stringFromBytes":
|
case "runtime.stringFromBytes":
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
|
|||||||
@@ -74,14 +74,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||||
if arch := b.archFamily(); arch != "arm" {
|
|
||||||
// Some targets pretend to be linux/arm for compatibility but aren't
|
|
||||||
// actually such a system. Make sure we emit an error instead of
|
|
||||||
// creating inline assembly that will fail to compile.
|
|
||||||
// See: https://github.com/tinygo-org/tinygo/issues/4959
|
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "system calls are not supported: target emulates a linux/arm system on "+arch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
|
|||||||
-235
@@ -270,241 +270,6 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%deferPtr = alloca ptr, align 4
|
|
||||||
store ptr null, ptr %deferPtr, align 4
|
|
||||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
|
||||||
%0 = call ptr @llvm.stacksave.p0()
|
|
||||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
|
||||||
br label %for.body
|
|
||||||
|
|
||||||
for.body: ; preds = %for.body, %entry
|
|
||||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
|
||||||
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
|
|
||||||
store i32 0, ptr %defer.alloc.call, align 4
|
|
||||||
%defer.alloc.call.repack1 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
|
|
||||||
store ptr %defer.next, ptr %defer.alloc.call.repack1, align 4
|
|
||||||
%defer.alloc.call.repack3 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
|
|
||||||
store i32 8, ptr %defer.alloc.call.repack3, align 4
|
|
||||||
store ptr %defer.alloc.call, ptr %deferPtr, align 4
|
|
||||||
br label %for.body
|
|
||||||
|
|
||||||
recover: ; preds = %rundefers.end
|
|
||||||
ret void
|
|
||||||
|
|
||||||
lpad: ; No predecessors!
|
|
||||||
br label %rundefers.loophead
|
|
||||||
|
|
||||||
rundefers.loophead: ; preds = %rundefers.callback0, %lpad
|
|
||||||
br i1 poison, label %rundefers.end, label %rundefers.loop
|
|
||||||
|
|
||||||
rundefers.loop: ; preds = %rundefers.loophead
|
|
||||||
switch i32 poison, label %rundefers.default [
|
|
||||||
i32 0, label %rundefers.callback0
|
|
||||||
]
|
|
||||||
|
|
||||||
rundefers.callback0: ; preds = %rundefers.loop
|
|
||||||
br label %rundefers.loophead
|
|
||||||
|
|
||||||
rundefers.default: ; preds = %rundefers.loop
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
rundefers.end: ; preds = %rundefers.loophead
|
|
||||||
br label %recover
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%deferPtr = alloca ptr, align 4
|
|
||||||
store ptr null, ptr %deferPtr, align 4
|
|
||||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
|
||||||
%0 = call ptr @llvm.stacksave.p0()
|
|
||||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
|
||||||
br label %for.loop
|
|
||||||
|
|
||||||
for.loop: ; preds = %for.body, %entry
|
|
||||||
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
|
|
||||||
%2 = icmp slt i32 %1, 10
|
|
||||||
br i1 %2, label %for.body, label %for.done
|
|
||||||
|
|
||||||
for.body: ; preds = %for.loop
|
|
||||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
|
||||||
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
|
|
||||||
store i32 0, ptr %defer.alloc.call, align 4
|
|
||||||
%defer.alloc.call.repack13 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
|
|
||||||
store ptr %defer.next, ptr %defer.alloc.call.repack13, align 4
|
|
||||||
%defer.alloc.call.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
|
|
||||||
store i32 %1, ptr %defer.alloc.call.repack15, align 4
|
|
||||||
store ptr %defer.alloc.call, ptr %deferPtr, align 4
|
|
||||||
%3 = add i32 %1, 1
|
|
||||||
br label %for.loop
|
|
||||||
|
|
||||||
for.done: ; preds = %for.loop
|
|
||||||
br label %rundefers.block
|
|
||||||
|
|
||||||
rundefers.after: ; preds = %rundefers.end
|
|
||||||
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
|
|
||||||
ret void
|
|
||||||
|
|
||||||
rundefers.block: ; preds = %for.done
|
|
||||||
br label %rundefers.loophead
|
|
||||||
|
|
||||||
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
|
|
||||||
%4 = load ptr, ptr %deferPtr, align 4
|
|
||||||
%stackIsNil = icmp eq ptr %4, null
|
|
||||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
|
||||||
|
|
||||||
rundefers.loop: ; preds = %rundefers.loophead
|
|
||||||
%stack.next.gep = getelementptr inbounds nuw i8, ptr %4, i32 4
|
|
||||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
|
||||||
store ptr %stack.next, ptr %deferPtr, align 4
|
|
||||||
%callback = load i32, ptr %4, align 4
|
|
||||||
switch i32 %callback, label %rundefers.default [
|
|
||||||
i32 0, label %rundefers.callback0
|
|
||||||
]
|
|
||||||
|
|
||||||
rundefers.callback0: ; preds = %rundefers.loop
|
|
||||||
%gep = getelementptr inbounds nuw i8, ptr %4, i32 8
|
|
||||||
%param = load i32, ptr %gep, align 4
|
|
||||||
call void @runtime.printlock(ptr undef) #4
|
|
||||||
call void @runtime.printint32(i32 %param, ptr undef) #4
|
|
||||||
call void @runtime.printunlock(ptr undef) #4
|
|
||||||
br label %rundefers.loophead
|
|
||||||
|
|
||||||
rundefers.default: ; preds = %rundefers.loop
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
rundefers.end: ; preds = %rundefers.loophead
|
|
||||||
br label %rundefers.after
|
|
||||||
|
|
||||||
recover: ; preds = %rundefers.end1
|
|
||||||
ret void
|
|
||||||
|
|
||||||
lpad: ; No predecessors!
|
|
||||||
br label %rundefers.loophead4
|
|
||||||
|
|
||||||
rundefers.loophead4: ; preds = %rundefers.callback010, %lpad
|
|
||||||
br i1 poison, label %rundefers.end1, label %rundefers.loop3
|
|
||||||
|
|
||||||
rundefers.loop3: ; preds = %rundefers.loophead4
|
|
||||||
switch i32 poison, label %rundefers.default2 [
|
|
||||||
i32 0, label %rundefers.callback010
|
|
||||||
]
|
|
||||||
|
|
||||||
rundefers.callback010: ; preds = %rundefers.loop3
|
|
||||||
br label %rundefers.loophead4
|
|
||||||
|
|
||||||
rundefers.default2: ; preds = %rundefers.loop3
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
rundefers.end1: ; preds = %rundefers.loophead4
|
|
||||||
br label %recover
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%defer.alloca = alloca { i32, ptr, i32 }, align 4
|
|
||||||
%deferPtr = alloca ptr, align 4
|
|
||||||
store ptr null, ptr %deferPtr, align 4
|
|
||||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
|
||||||
%0 = call ptr @llvm.stacksave.p0()
|
|
||||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
|
||||||
br label %for.loop
|
|
||||||
|
|
||||||
for.loop: ; preds = %for.body, %entry
|
|
||||||
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
|
|
||||||
%2 = icmp slt i32 %1, 10
|
|
||||||
br i1 %2, label %for.body, label %for.done
|
|
||||||
|
|
||||||
for.body: ; preds = %for.loop
|
|
||||||
%3 = add i32 %1, 1
|
|
||||||
br label %for.loop
|
|
||||||
|
|
||||||
for.done: ; preds = %for.loop
|
|
||||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
|
||||||
store i32 0, ptr %defer.alloca, align 4
|
|
||||||
%defer.alloca.repack16 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
|
||||||
store ptr %defer.next, ptr %defer.alloca.repack16, align 4
|
|
||||||
%defer.alloca.repack18 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 8
|
|
||||||
store i32 1, ptr %defer.alloca.repack18, align 4
|
|
||||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
|
||||||
br label %for.loop1
|
|
||||||
|
|
||||||
for.loop1: ; preds = %for.body2, %for.done
|
|
||||||
%4 = phi i32 [ 0, %for.done ], [ %6, %for.body2 ]
|
|
||||||
%5 = icmp slt i32 %4, 10
|
|
||||||
br i1 %5, label %for.body2, label %for.done3
|
|
||||||
|
|
||||||
for.body2: ; preds = %for.loop1
|
|
||||||
%6 = add i32 %4, 1
|
|
||||||
br label %for.loop1
|
|
||||||
|
|
||||||
for.done3: ; preds = %for.loop1
|
|
||||||
br label %rundefers.block
|
|
||||||
|
|
||||||
rundefers.after: ; preds = %rundefers.end
|
|
||||||
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
|
|
||||||
ret void
|
|
||||||
|
|
||||||
rundefers.block: ; preds = %for.done3
|
|
||||||
br label %rundefers.loophead
|
|
||||||
|
|
||||||
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
|
|
||||||
%7 = load ptr, ptr %deferPtr, align 4
|
|
||||||
%stackIsNil = icmp eq ptr %7, null
|
|
||||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
|
||||||
|
|
||||||
rundefers.loop: ; preds = %rundefers.loophead
|
|
||||||
%stack.next.gep = getelementptr inbounds nuw i8, ptr %7, i32 4
|
|
||||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
|
||||||
store ptr %stack.next, ptr %deferPtr, align 4
|
|
||||||
%callback = load i32, ptr %7, align 4
|
|
||||||
switch i32 %callback, label %rundefers.default [
|
|
||||||
i32 0, label %rundefers.callback0
|
|
||||||
]
|
|
||||||
|
|
||||||
rundefers.callback0: ; preds = %rundefers.loop
|
|
||||||
%gep = getelementptr inbounds nuw i8, ptr %7, i32 8
|
|
||||||
%param = load i32, ptr %gep, align 4
|
|
||||||
call void @runtime.printlock(ptr undef) #4
|
|
||||||
call void @runtime.printint32(i32 %param, ptr undef) #4
|
|
||||||
call void @runtime.printunlock(ptr undef) #4
|
|
||||||
br label %rundefers.loophead
|
|
||||||
|
|
||||||
rundefers.default: ; preds = %rundefers.loop
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
rundefers.end: ; preds = %rundefers.loophead
|
|
||||||
br label %rundefers.after
|
|
||||||
|
|
||||||
recover: ; preds = %rundefers.end4
|
|
||||||
ret void
|
|
||||||
|
|
||||||
lpad: ; No predecessors!
|
|
||||||
br label %rundefers.loophead7
|
|
||||||
|
|
||||||
rundefers.loophead7: ; preds = %rundefers.callback013, %lpad
|
|
||||||
br i1 poison, label %rundefers.end4, label %rundefers.loop6
|
|
||||||
|
|
||||||
rundefers.loop6: ; preds = %rundefers.loophead7
|
|
||||||
switch i32 poison, label %rundefers.default5 [
|
|
||||||
i32 0, label %rundefers.callback013
|
|
||||||
]
|
|
||||||
|
|
||||||
rundefers.callback013: ; preds = %rundefers.loop6
|
|
||||||
br label %rundefers.loophead7
|
|
||||||
|
|
||||||
rundefers.default5: ; preds = %rundefers.loop6
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
rundefers.end4: ; preds = %rundefers.loophead7
|
|
||||||
br label %recover
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
|
|||||||
Vendored
-20
@@ -18,23 +18,3 @@ func deferMultiple() {
|
|||||||
}()
|
}()
|
||||||
external()
|
external()
|
||||||
}
|
}
|
||||||
|
|
||||||
func deferInfiniteLoop() {
|
|
||||||
for {
|
|
||||||
defer print(8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deferLoop() {
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
defer print(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deferBetweenLoops() {
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
}
|
|
||||||
defer print(1)
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
-8
@@ -24,10 +24,6 @@ var (
|
|||||||
x *byte
|
x *byte
|
||||||
y [61]uintptr
|
y [61]uintptr
|
||||||
}
|
}
|
||||||
struct5 *struct {
|
|
||||||
x *byte
|
|
||||||
y [30]uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
slice1 []byte
|
slice1 []byte
|
||||||
slice2 []*int
|
slice2 []*int
|
||||||
@@ -62,10 +58,6 @@ func newStruct() {
|
|||||||
x *byte
|
x *byte
|
||||||
y [61]uintptr
|
y [61]uintptr
|
||||||
})
|
})
|
||||||
struct5 = new(struct {
|
|
||||||
x *byte
|
|
||||||
y [30]uintptr
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFuncValue() *func() {
|
func newFuncValue() *func() {
|
||||||
|
|||||||
Vendored
+4
-8
@@ -16,12 +16,11 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@main.struct2 = hidden global ptr null, align 4
|
@main.struct2 = hidden global ptr null, align 4
|
||||||
@main.struct3 = hidden global ptr null, align 4
|
@main.struct3 = hidden global ptr null, align 4
|
||||||
@main.struct4 = hidden global ptr null, align 4
|
@main.struct4 = hidden global ptr null, align 4
|
||||||
@main.struct5 = hidden global ptr null, align 4
|
|
||||||
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@"runtime/gc.layout:62-0100000000000020" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||||
@"runtime/gc.layout:62-0100000000000000" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||||
|
|
||||||
@@ -81,15 +80,12 @@ entry:
|
|||||||
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new1, ptr @main.struct2, align 4
|
store ptr %new1, ptr @main.struct2, align 4
|
||||||
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000020", ptr undef) #3
|
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new2, ptr @main.struct3, align 4
|
store ptr %new2, ptr @main.struct3, align 4
|
||||||
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000000", ptr undef) #3
|
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new3, ptr @main.struct4, align 4
|
store ptr %new3, ptr @main.struct4, align 4
|
||||||
%new4 = call align 4 dereferenceable(124) ptr @runtime.alloc(i32 124, ptr nonnull inttoptr (i32 127 to ptr), ptr undef) #3
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %new4, ptr nonnull %stackalloc, ptr undef) #3
|
|
||||||
store ptr %new4, ptr @main.struct5, align 4
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -50,7 +50,7 @@ unsafe.String.throw: ; preds = %entry
|
|||||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
|
|||||||
Vendored
+30
-36
@@ -22,9 +22,6 @@ entry:
|
|||||||
ret i32 %a
|
ret i32 %a
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i32 @llvm.smin.i32(i32, i32) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
@@ -56,9 +53,6 @@ entry:
|
|||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i8 @llvm.umin.i8(i8, i8) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
@@ -66,31 +60,24 @@ entry:
|
|||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i32 @llvm.umin.i32(i32, i32) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call float @llvm.minimum.f32(float %a, float %b)
|
%0 = fcmp olt float %a, %b
|
||||||
ret float %0
|
%1 = select i1 %0, float %a, float %b
|
||||||
|
ret float %1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare float @llvm.minimum.f32(float, float) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call double @llvm.minimum.f64(double %a, double %b)
|
%0 = fcmp olt double %a, %b
|
||||||
ret double %0
|
%1 = select i1 %0, double %a, double %b
|
||||||
|
ret double %1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare double @llvm.minimum.f64(double, double) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
@@ -104,7 +91,7 @@ entry:
|
|||||||
ret %runtime._string %5
|
ret %runtime._string %5
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
@@ -113,9 +100,6 @@ entry:
|
|||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i32 @llvm.smax.i32(i32, i32) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
@@ -123,21 +107,16 @@ entry:
|
|||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i32 @llvm.umax.i32(i32, i32) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call float @llvm.maximum.f32(float %a, float %b)
|
%0 = fcmp ogt float %a, %b
|
||||||
ret float %0
|
%1 = select i1 %0, float %a, float %b
|
||||||
|
ret float %1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare float @llvm.maximum.f32(float, float) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
@@ -160,7 +139,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
|
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
|
||||||
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #4
|
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||||
@@ -177,9 +156,24 @@ entry:
|
|||||||
|
|
||||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.smin.i32(i32, i32) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i8 @llvm.umin.i8(i8, i8) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.umin.i32(i32, i32) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.smax.i32(i32, i32) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.umax.i32(i32, i32) #4
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #5 = { nounwind }
|
attributes #5 = { nounwind }
|
||||||
|
|||||||
+28
-35
@@ -17,8 +17,8 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ declare void @main.regularFunction(i32, ptr) #2
|
|||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +39,8 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,18 +61,18 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||||
store i32 3, ptr %n, align 4
|
store i32 3, ptr %n, align 4
|
||||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %n, ptr %1, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
||||||
%2 = load i32, ptr %n, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
call void @runtime.printlock(ptr undef) #11
|
call void @runtime.printlock(ptr undef) #9
|
||||||
call void @runtime.printint32(i32 %2, ptr undef) #11
|
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||||
call void @runtime.printunlock(ptr undef) #11
|
call void @runtime.printunlock(ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,14 +102,14 @@ declare void @runtime.printunlock(ptr) #2
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %fn.context, ptr %1, align 4
|
store ptr %fn.context, ptr %1, align 4
|
||||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
store ptr %fn.funcptr, ptr %2, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #11
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ entry:
|
|||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
%5 = load ptr, ptr %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
call void %5(i32 %1, ptr %3) #11
|
call void %5(i32 %1, ptr %3) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,21 +134,16 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
|
||||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2
|
||||||
declare i32 @llvm.umin.i32(i32, i32) #7
|
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
|
||||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +152,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
||||||
store ptr %itf.value, ptr %0, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr @"main$string", ptr %1, align 4
|
store ptr @"main$string", ptr %1, align 4
|
||||||
@@ -165,15 +160,15 @@ entry:
|
|||||||
store i32 4, ptr %2, align 4
|
store i32 4, ptr %2, align 4
|
||||||
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
store ptr %itf.typecode, ptr %3, align 4
|
store ptr %itf.typecode, ptr %3, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #11
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load ptr, ptr %0, align 4
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
@@ -182,7 +177,7 @@ entry:
|
|||||||
%5 = load i32, ptr %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
%7 = load ptr, ptr %6, align 4
|
%7 = load ptr, ptr %6, align 4
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,8 +188,6 @@ attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb
|
|||||||
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #7 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
attributes #8 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #9 = { nounwind }
|
||||||
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
|
||||||
attributes #11 = { nounwind }
|
|
||||||
|
|||||||
+34
-41
@@ -19,7 +19,7 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ declare void @runtime.deadlock(ptr) #1
|
|||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
|
||||||
call void @runtime.deadlock(ptr undef) #11
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unn
|
|||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||||
call void @runtime.deadlock(ptr undef) #11
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,21 +64,21 @@ entry:
|
|||||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11
|
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
store i32 3, ptr %n, align 4
|
store i32 3, ptr %n, align 4
|
||||||
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11
|
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #11
|
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #9
|
||||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %n, ptr %1, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
%2 = load i32, ptr %n, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
call void @runtime.printlock(ptr undef) #11
|
call void @runtime.printlock(ptr undef) #9
|
||||||
call void @runtime.printint32(i32 %2, ptr undef) #11
|
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||||
call void @runtime.printunlock(ptr undef) #11
|
call void @runtime.printunlock(ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ entry:
|
|||||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||||
call void @runtime.deadlock(ptr undef) #11
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,14 +110,14 @@ declare void @runtime.printunlock(ptr) #1
|
|||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %fn.context, ptr %1, align 4
|
store ptr %fn.context, ptr %1, align 4
|
||||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
store ptr %fn.funcptr, ptr %2, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +129,8 @@ entry:
|
|||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
%5 = load ptr, ptr %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
call void %5(i32 %1, ptr %3) #11
|
call void %5(i32 %1, ptr %3) #9
|
||||||
call void @runtime.deadlock(ptr undef) #11
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,21 +143,16 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
|
||||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
|
||||||
declare i32 @llvm.umin.i32(i32, i32) #7
|
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
|
||||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,8 +162,8 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
|||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
store ptr %itf.value, ptr %0, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr @"main$string", ptr %1, align 4
|
store ptr @"main$string", ptr %1, align 4
|
||||||
@@ -176,14 +171,14 @@ entry:
|
|||||||
store i32 4, ptr %2, align 4
|
store i32 4, ptr %2, align 4
|
||||||
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
store ptr %itf.typecode, ptr %3, align 4
|
store ptr %itf.typecode, ptr %3, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load ptr, ptr %0, align 4
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
@@ -192,8 +187,8 @@ entry:
|
|||||||
%5 = load i32, ptr %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
%7 = load ptr, ptr %6, align 4
|
%7 = load ptr, ptr %6, align 4
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
||||||
call void @runtime.deadlock(ptr undef) #11
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +199,6 @@ attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+cal
|
|||||||
attributes #4 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
||||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
attributes #8 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #9 = { nounwind }
|
||||||
attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
|
||||||
attributes #11 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+36
-44
@@ -38,7 +38,7 @@ lookup.next: ; preds = %entry
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
lookup.throw: ; preds = %entry
|
lookup.throw: ; preds = %entry
|
||||||
call void @runtime.lookupPanic(ptr undef) #5
|
call void @runtime.lookupPanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,55 +48,49 @@ declare void @runtime.lookupPanic(ptr) #1
|
|||||||
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store i32 1, ptr %varargs, align 4
|
store i32 1, ptr %varargs, align 4
|
||||||
%0 = getelementptr inbounds nuw i8, ptr %varargs, i32 4
|
%0 = getelementptr inbounds nuw i8, ptr %varargs, i32 4
|
||||||
store i32 2, ptr %0, align 4
|
store i32 2, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %varargs, i32 8
|
%1 = getelementptr inbounds nuw i8, ptr %varargs, i32 8
|
||||||
store i32 3, ptr %1, align 4
|
store i32 3, ptr %1, align 4
|
||||||
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr undef) #3
|
||||||
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
||||||
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
||||||
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
||||||
%2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
%2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
||||||
%3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1
|
%3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1
|
||||||
%4 = insertvalue { ptr, i32, i32 } %3, i32 %append.newCap, 2
|
%4 = insertvalue { ptr, i32, i32 } %3, i32 %append.newCap, 2
|
||||||
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %4
|
ret { ptr, i32, i32 } %4
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1
|
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr undef) #3
|
||||||
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
||||||
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
||||||
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
||||||
%0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
||||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1
|
||||||
%2 = insertvalue { ptr, i32, i32 } %1, i32 %append.newCap, 2
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %append.newCap, 2
|
||||||
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %2
|
ret { ptr, i32, i32 } %2
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 4, ptr undef) #3
|
||||||
%copy.size = shl nuw i32 %copy.n, 2
|
|
||||||
call void @llvm.memmove.p0.p0.i32(ptr align 4 %dst.data, ptr align 4 %src.data, i32 %copy.size, i1 false)
|
|
||||||
ret i32 %copy.n
|
ret i32 %copy.n
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
|
||||||
declare i32 @llvm.umin.i32(i32, i32) #3
|
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
|
||||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #4
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||||
@@ -106,15 +100,15 @@ entry:
|
|||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %2
|
ret { ptr, i32, i32 } %2
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(ptr undef) #5
|
call void @runtime.slicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,15 +123,15 @@ entry:
|
|||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = shl nuw i32 %len, 1
|
%makeslice.cap = shl nuw i32 %len, 1
|
||||||
%makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %2
|
ret { ptr, i32, i32 } %2
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(ptr undef) #5
|
call void @runtime.slicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,15 +144,15 @@ entry:
|
|||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = mul i32 %len, 3
|
%makeslice.cap = mul i32 %len, 3
|
||||||
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %2
|
ret { ptr, i32, i32 } %2
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(ptr undef) #5
|
call void @runtime.slicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,15 +165,15 @@ entry:
|
|||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = shl nuw i32 %len, 2
|
%makeslice.cap = shl nuw i32 %len, 2
|
||||||
%makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %2
|
ret { ptr, i32, i32 } %2
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(ptr undef) #5
|
call void @runtime.slicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +182,7 @@ define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
|
|||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = getelementptr i8, ptr %p, i32 %len
|
%0 = getelementptr i8, ptr %p, i32 %len
|
||||||
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret ptr %0
|
ret ptr %0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +192,7 @@ entry:
|
|||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = trunc i64 %len to i32
|
%0 = trunc i64 %len to i32
|
||||||
%1 = getelementptr i8, ptr %p, i32 %0
|
%1 = getelementptr i8, ptr %p, i32 %0
|
||||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret ptr %1
|
ret ptr %1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +206,7 @@ slicetoarray.next: ; preds = %entry
|
|||||||
ret ptr %s.data
|
ret ptr %s.data
|
||||||
|
|
||||||
slicetoarray.throw: ; preds = %entry
|
slicetoarray.throw: ; preds = %entry
|
||||||
call void @runtime.sliceToArrayPointerPanic(ptr undef) #5
|
call void @runtime.sliceToArrayPointerPanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,8 +216,8 @@ declare void @runtime.sliceToArrayPointerPanic(ptr) #1
|
|||||||
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
slicetoarray.next: ; preds = %entry
|
slicetoarray.next: ; preds = %entry
|
||||||
@@ -248,11 +242,11 @@ unsafe.Slice.next: ; preds = %entry
|
|||||||
%5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
%5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
%6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1
|
%6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1
|
||||||
%7 = insertvalue { ptr, i32, i32 } %6, i32 %len, 2
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %len, 2
|
||||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %7
|
ret { ptr, i32, i32 } %7
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,11 +266,11 @@ unsafe.Slice.next: ; preds = %entry
|
|||||||
%4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
%4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
%5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1
|
%5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1
|
||||||
%6 = insertvalue { ptr, i32, i32 } %5, i32 %3, 2
|
%6 = insertvalue { ptr, i32, i32 } %5, i32 %3, 2
|
||||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %6
|
ret { ptr, i32, i32 } %6
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,11 +290,11 @@ unsafe.Slice.next: ; preds = %entry
|
|||||||
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||||
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
||||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %8
|
ret { ptr, i32, i32 } %8
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,17 +314,15 @@ unsafe.Slice.next: ; preds = %entry
|
|||||||
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||||
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
||||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
ret { ptr, i32, i32 } %8
|
ret { ptr, i32, i32 } %8
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #3 = { nounwind }
|
||||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
|
||||||
attributes #5 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+8
-8
@@ -31,13 +31,13 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %s.len
|
ret i32 %s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%.not = icmp ult i32 %index, %s.len
|
%.not = icmp ult i32 %index, %s.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
@@ -55,16 +55,16 @@ lookup.throw: ; preds = %entry
|
|||||||
declare void @runtime.lookupPanic(ptr) #1
|
declare void @runtime.lookupPanic(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||||
%1 = xor i1 %0, true
|
%1 = xor i1 %0, true
|
||||||
@@ -72,16 +72,16 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%0 = zext i8 %x to i32
|
%0 = zext i8 %x to i32
|
||||||
%.not = icmp ugt i32 %s.len, %0
|
%.not = icmp ugt i32 %s.len, %0
|
||||||
|
|||||||
Generated
+4
-4
@@ -20,16 +20,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1770136044,
|
"lastModified": 1747953325,
|
||||||
"narHash": "sha256-tlFqNG/uzz2++aAmn4v8J0vAkV3z7XngeIIB3rM3650=",
|
"narHash": "sha256-y2ZtlIlNTuVJUZCqzZAhIw5rrKP4DOSklev6c8PyCkQ=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e576e3c9cf9bad747afcddd9e34f51d18c855b4e",
|
"rev": "55d1f923c480dadce40f5231feb472e81b0bab48",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
"ref": "nixos-25.11",
|
"ref": "nixos-25.05",
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
# Use a recent stable release, but fix the version to make it reproducible.
|
# Use a recent stable release, but fix the version to make it reproducible.
|
||||||
# This version should be updated from time to time.
|
# This version should be updated from time to time.
|
||||||
nixpkgs.url = "nixpkgs/nixos-25.11";
|
nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,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.41.0-dev"
|
const version = "0.40.0-dev"
|
||||||
|
|
||||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||||
// (like 0.30.0-dev-abcd012).
|
// (like 0.30.0-dev-abcd012).
|
||||||
|
|||||||
+11
-2
@@ -2,6 +2,7 @@ package interp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -10,17 +11,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInterp(t *testing.T) {
|
func TestInterp(t *testing.T) {
|
||||||
|
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||||
|
if err != nil {
|
||||||
|
// Note: this should never happen and if it does, it will always happen
|
||||||
|
// for a particular build because llvm.Version is a constant.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
for _, name := range []string{
|
for _, name := range []string{
|
||||||
"basic",
|
"basic",
|
||||||
"phi",
|
"phi",
|
||||||
|
"slice-copy",
|
||||||
"consteval",
|
"consteval",
|
||||||
"intrinsics",
|
|
||||||
"copy",
|
|
||||||
"interface",
|
"interface",
|
||||||
"revert",
|
"revert",
|
||||||
"alloc",
|
"alloc",
|
||||||
} {
|
} {
|
||||||
name := name // make local to this closure
|
name := name // make local to this closure
|
||||||
|
if name == "slice-copy" && llvmVersion < 14 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest(t, "testdata/"+name)
|
runTest(t, "testdata/"+name)
|
||||||
|
|||||||
+51
-22
@@ -312,28 +312,33 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
fmt.Fprintln(os.Stderr, indent+"runtime.alloc:", size, "->", ptr)
|
fmt.Fprintln(os.Stderr, indent+"runtime.alloc:", size, "->", ptr)
|
||||||
}
|
}
|
||||||
locals[inst.localIndex] = ptr
|
locals[inst.localIndex] = ptr
|
||||||
case strings.HasPrefix(callFn.name, "llvm.umin."):
|
case callFn.name == "runtime.sliceCopy":
|
||||||
locals[inst.localIndex] = makeLiteralInt(min(operands[1].Uint(r), operands[2].Uint(r)), inst.llvmInst.Type().IntTypeWidth())
|
// sliceCopy implements the built-in copy function for slices.
|
||||||
case strings.HasPrefix(callFn.name, "llvm.smin."):
|
// It is implemented here so that it can be used even if the
|
||||||
locals[inst.localIndex] = makeLiteralInt(uint64(min(operands[1].Int(r), operands[2].Int(r))), inst.llvmInst.Type().IntTypeWidth())
|
// runtime implementation is not available. Doing it this way
|
||||||
case strings.HasPrefix(callFn.name, "llvm.umax."):
|
// may also be faster.
|
||||||
locals[inst.localIndex] = makeLiteralInt(max(operands[1].Uint(r), operands[2].Uint(r)), inst.llvmInst.Type().IntTypeWidth())
|
// Code:
|
||||||
case strings.HasPrefix(callFn.name, "llvm.smax."):
|
// func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int {
|
||||||
locals[inst.localIndex] = makeLiteralInt(uint64(max(operands[1].Int(r), operands[2].Int(r))), inst.llvmInst.Type().IntTypeWidth())
|
// n := srcLen
|
||||||
case strings.HasPrefix(callFn.name, "llvm.memcpy.p0") || strings.HasPrefix(callFn.name, "llvm.memmove.p0"):
|
// if n > dstLen {
|
||||||
// Copy a block of memory from one pointer to another.
|
// n = dstLen
|
||||||
if operands[4].Uint(r) != 0 {
|
// }
|
||||||
// This is a volatile copy/move.
|
// memmove(dst, src, n*elemSize)
|
||||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
// return int(n)
|
||||||
if err != nil {
|
// }
|
||||||
return nil, mem, err
|
dstLen := operands[3].Uint(r)
|
||||||
}
|
srcLen := operands[4].Uint(r)
|
||||||
continue
|
elemSize := operands[5].Uint(r)
|
||||||
|
n := srcLen
|
||||||
|
if n > dstLen {
|
||||||
|
n = dstLen
|
||||||
}
|
}
|
||||||
nBytes := operands[3].Uint(r)
|
if r.debug {
|
||||||
if nBytes != 0 {
|
fmt.Fprintln(os.Stderr, indent+"copy:", operands[1], operands[2], n)
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
// Only try to copy bytes when there are any bytes to copy.
|
// Only try to copy bytes when there are any bytes to copy.
|
||||||
// This is not just an optimization. If one of the pointers
|
// This is not just an optimization. If one of the slices
|
||||||
// (or both) are nil, the asPointer method call will fail
|
// (or both) are nil, the asPointer method call will fail
|
||||||
// even though copying a nil slice is allowed.
|
// even though copying a nil slice is allowed.
|
||||||
dst, err := operands[1].asPointer(r)
|
dst, err := operands[1].asPointer(r)
|
||||||
@@ -358,10 +363,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
nBytes := uint32(n * elemSize)
|
||||||
srcObj := mem.get(src.index())
|
srcObj := mem.get(src.index())
|
||||||
dstObj := mem.getWritable(dst.index())
|
dstObj := mem.getWritable(dst.index())
|
||||||
if srcObj.buffer == nil || dstObj.buffer == nil {
|
if srcObj.buffer == nil || dstObj.buffer == nil {
|
||||||
// If the buffer is nil, it means the memory is external.
|
// If the buffer is nil, it means the slice is external.
|
||||||
// This can happen for example when copying data out of
|
// This can happen for example when copying data out of
|
||||||
// a //go:embed slice, which is not available at interp
|
// a //go:embed slice, which is not available at interp
|
||||||
// time.
|
// time.
|
||||||
@@ -374,10 +380,33 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
dstBuf := dstObj.buffer.asRawValue(r)
|
dstBuf := dstObj.buffer.asRawValue(r)
|
||||||
srcBuf := srcObj.buffer.asRawValue(r)
|
srcBuf := srcObj.buffer.asRawValue(r)
|
||||||
copy(dstBuf.buf[dst.offset():][:nBytes], srcBuf.buf[src.offset():][:nBytes])
|
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
||||||
dstObj.buffer = dstBuf
|
dstObj.buffer = dstBuf
|
||||||
mem.put(dst.index(), dstObj)
|
mem.put(dst.index(), dstObj)
|
||||||
}
|
}
|
||||||
|
locals[inst.localIndex] = makeLiteralInt(n, inst.llvmInst.Type().IntTypeWidth())
|
||||||
|
case strings.HasPrefix(callFn.name, "llvm.memcpy.p0") || strings.HasPrefix(callFn.name, "llvm.memmove.p0"):
|
||||||
|
// Copy a block of memory from one pointer to another.
|
||||||
|
dst, err := operands[1].asPointer(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
src, err := operands[2].asPointer(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
nBytes := uint32(operands[3].Uint(r))
|
||||||
|
dstObj := mem.getWritable(dst.index())
|
||||||
|
dstBuf := dstObj.buffer.asRawValue(r)
|
||||||
|
if mem.get(src.index()).buffer == nil {
|
||||||
|
// Looks like the source buffer is not defined.
|
||||||
|
// This can happen with //extern or //go:embed.
|
||||||
|
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
|
||||||
|
}
|
||||||
|
srcBuf := mem.get(src.index()).buffer.asRawValue(r)
|
||||||
|
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
||||||
|
dstObj.buffer = dstBuf
|
||||||
|
mem.put(dst.index(), dstObj)
|
||||||
case callFn.name == "runtime.typeAssert":
|
case callFn.name == "runtime.typeAssert":
|
||||||
// This function must be implemented manually as it is normally
|
// This function must be implemented manually as it is normally
|
||||||
// implemented by the interface lowering pass.
|
// implemented by the interface lowering pass.
|
||||||
|
|||||||
Vendored
-68
@@ -1,68 +0,0 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|
||||||
target triple = "x86_64--linux"
|
|
||||||
|
|
||||||
@string = internal unnamed_addr constant [3 x i8] c"foo"
|
|
||||||
@moveDst = global [3 x i8] zeroinitializer
|
|
||||||
@copyDst = global [3 x i8] zeroinitializer
|
|
||||||
|
|
||||||
@externalSrc = external global [2 x i8]
|
|
||||||
@moveExternalDst = global [2 x i8] zeroinitializer
|
|
||||||
|
|
||||||
@moveEscapedSrc = global [4 x i8] c"abcd"
|
|
||||||
@moveEscapedDst = global [4 x i8] zeroinitializer
|
|
||||||
|
|
||||||
@volatileSrc = global [2 x i8] c"xy"
|
|
||||||
@volatileDst = global [2 x i8] zeroinitializer
|
|
||||||
|
|
||||||
declare void @use(ptr)
|
|
||||||
|
|
||||||
define void @runtime.initAll() {
|
|
||||||
call void @main.init()
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
define internal void @main.init() {
|
|
||||||
call void @testMove()
|
|
||||||
call void @testCopy()
|
|
||||||
call void @testMoveExternal()
|
|
||||||
call void @testMoveEscaped()
|
|
||||||
call void @testVolatileCopy()
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Test a simple memmove between globals.
|
|
||||||
define internal void @testMove() {
|
|
||||||
call void @llvm.memmove.p0.p0.i64(ptr @moveDst, ptr @string, i64 3, i1 false)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Test a simple memcpy between globals.
|
|
||||||
define internal void @testCopy() {
|
|
||||||
call void @llvm.memcpy.p0.p0.i64(ptr @copyDst, ptr @string, i64 3, i1 false)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Test a memmove from an external global.
|
|
||||||
; This should be run at runtime.
|
|
||||||
define internal void @testMoveExternal() {
|
|
||||||
call void @llvm.memmove.p0.p0.i64(ptr @moveExternalDst, ptr @externalSrc, i64 2, i1 false)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Test a memmove from an escaped (and potentially modified) source buffer.
|
|
||||||
define internal void @testMoveEscaped() {
|
|
||||||
call void @use(ptr @moveEscapedSrc)
|
|
||||||
call void @llvm.memmove.p0.p0.i64(ptr @moveEscapedDst, ptr @moveEscapedSrc, i64 4, i1 false)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Test a volatile memcpy.
|
|
||||||
; This should always be run at runtime.
|
|
||||||
define internal void @testVolatileCopy() {
|
|
||||||
call void @llvm.memcpy.p0.p0.i64(ptr @volatileDst, ptr @volatileSrc, i64 2, i1 true)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare void @llvm.memmove.p0.p0.i64(ptr, ptr, i64, i1)
|
|
||||||
|
|
||||||
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
|
|
||||||
Vendored
-27
@@ -1,27 +0,0 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|
||||||
target triple = "x86_64--linux"
|
|
||||||
|
|
||||||
@moveDst = local_unnamed_addr global [3 x i8] c"foo"
|
|
||||||
@copyDst = local_unnamed_addr global [3 x i8] c"foo"
|
|
||||||
@externalSrc = external local_unnamed_addr global [2 x i8]
|
|
||||||
@moveExternalDst = local_unnamed_addr global [2 x i8] zeroinitializer
|
|
||||||
@moveEscapedSrc = global [4 x i8] c"abcd"
|
|
||||||
@moveEscapedDst = local_unnamed_addr global [4 x i8] zeroinitializer
|
|
||||||
@volatileSrc = global [2 x i8] c"xy"
|
|
||||||
@volatileDst = global [2 x i8] zeroinitializer
|
|
||||||
|
|
||||||
declare void @use(ptr) local_unnamed_addr
|
|
||||||
|
|
||||||
define void @runtime.initAll() local_unnamed_addr {
|
|
||||||
call void @llvm.memmove.p0.p0.i64(ptr @moveExternalDst, ptr @externalSrc, i64 2, i1 false)
|
|
||||||
call void @use(ptr @moveEscapedSrc)
|
|
||||||
call void @llvm.memmove.p0.p0.i64(ptr @moveEscapedDst, ptr @moveEscapedSrc, i64 4, i1 false)
|
|
||||||
call void @llvm.memcpy.p0.p0.i64(ptr @volatileDst, ptr @volatileSrc, i64 2, i1 true)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare void @llvm.memmove.p0.p0.i64(ptr nocapture writeonly, ptr nocapture readonly, i64, i1 immarg) #0
|
|
||||||
|
|
||||||
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
|
|
||||||
|
|
||||||
attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
|
||||||
Vendored
-52
@@ -1,52 +0,0 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|
||||||
target triple = "x86_64--linux"
|
|
||||||
|
|
||||||
@uminResult = global i32 0
|
|
||||||
@sminResult = global i32 0
|
|
||||||
@umaxResult = global i32 0
|
|
||||||
@smaxResult = global i32 0
|
|
||||||
|
|
||||||
define void @runtime.initAll() {
|
|
||||||
call void @main.init()
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
define internal void @main.init() {
|
|
||||||
call void @testUMin()
|
|
||||||
call void @testSMin()
|
|
||||||
call void @testUMax()
|
|
||||||
call void @testSMax()
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
define internal void @testUMin() {
|
|
||||||
%umin = call i32 @llvm.umin.i32(i32 12, i32 -1)
|
|
||||||
store i32 %umin, ptr @uminResult
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare i32 @llvm.umin.i32(i32, i32)
|
|
||||||
|
|
||||||
define internal void @testSMin() {
|
|
||||||
%smin = call i32 @llvm.smin.i32(i32 12, i32 -1)
|
|
||||||
store i32 %smin, ptr @sminResult
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare i32 @llvm.smin.i32(i32, i32)
|
|
||||||
|
|
||||||
define internal void @testUMax() {
|
|
||||||
%umax = call i32 @llvm.umax.i32(i32 12, i32 -1)
|
|
||||||
store i32 %umax, ptr @umaxResult
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare i32 @llvm.umax.i32(i32, i32)
|
|
||||||
|
|
||||||
define internal void @testSMax() {
|
|
||||||
%smax = call i32 @llvm.smax.i32(i32 12, i32 -1)
|
|
||||||
store i32 %smax, ptr @smaxResult
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare i32 @llvm.smax.i32(i32, i32)
|
|
||||||
Vendored
-11
@@ -1,11 +0,0 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|
||||||
target triple = "x86_64--linux"
|
|
||||||
|
|
||||||
@uminResult = local_unnamed_addr global i32 12
|
|
||||||
@sminResult = local_unnamed_addr global i32 -1
|
|
||||||
@umaxResult = local_unnamed_addr global i32 -1
|
|
||||||
@smaxResult = local_unnamed_addr global i32 12
|
|
||||||
|
|
||||||
define void @runtime.initAll() local_unnamed_addr {
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
Vendored
+124
@@ -0,0 +1,124 @@
|
|||||||
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
|
@main.uint8SliceSrc.buf = internal global [2 x i8] c"\03d"
|
||||||
|
@main.uint8SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.uint8SliceSrc.buf, i64 2, i64 2 }
|
||||||
|
@main.uint8SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
|
||||||
|
@main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024]
|
||||||
|
@main.int16SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.int16SliceSrc.buf, i64 3, i64 3 }
|
||||||
|
@main.int16SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
|
||||||
|
@main.sliceSrcUntaint.buf = internal global [2 x i8] c"ab"
|
||||||
|
@main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
||||||
|
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal1.buf = external global [2 x i8]
|
||||||
|
@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceDstExternal2.buf = external global [2 x i8]
|
||||||
|
|
||||||
|
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
||||||
|
|
||||||
|
declare ptr @runtime.alloc(i64, ptr) unnamed_addr
|
||||||
|
|
||||||
|
declare void @runtime.printuint8(i8)
|
||||||
|
|
||||||
|
declare void @runtime.printint16(i16)
|
||||||
|
|
||||||
|
declare void @use(ptr)
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @main.init()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @main() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
; print(uintSliceSrc[0])
|
||||||
|
%uint8SliceSrc.buf = load ptr, ptr @main.uint8SliceSrc
|
||||||
|
%uint8SliceSrc.val = load i8, ptr %uint8SliceSrc.buf
|
||||||
|
call void @runtime.printuint8(i8 %uint8SliceSrc.val)
|
||||||
|
|
||||||
|
; print(uintSliceDst[0])
|
||||||
|
%uint8SliceDst.buf = load ptr, ptr @main.uint8SliceDst
|
||||||
|
%uint8SliceDst.val = load i8, ptr %uint8SliceDst.buf
|
||||||
|
call void @runtime.printuint8(i8 %uint8SliceDst.val)
|
||||||
|
|
||||||
|
; print(int16SliceSrc[0])
|
||||||
|
%int16SliceSrc.buf = load ptr, ptr @main.int16SliceSrc
|
||||||
|
%int16SliceSrc.val = load i16, ptr %int16SliceSrc.buf
|
||||||
|
call void @runtime.printint16(i16 %int16SliceSrc.val)
|
||||||
|
|
||||||
|
; print(int16SliceDst[0])
|
||||||
|
%int16SliceDst.buf = load ptr, ptr @main.int16SliceDst
|
||||||
|
%int16SliceDst.val = load i16, ptr %int16SliceDst.buf
|
||||||
|
call void @runtime.printint16(i16 %int16SliceDst.val)
|
||||||
|
|
||||||
|
; print(sliceDstUntaint[0])
|
||||||
|
%sliceDstUntaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstUntaint.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstUntaint.val)
|
||||||
|
|
||||||
|
; print(sliceDstTaint[0])
|
||||||
|
%sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
||||||
|
|
||||||
|
; print(sliceDstExternal1[0])
|
||||||
|
%sliceDstExternal1.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal1.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal1.val)
|
||||||
|
|
||||||
|
; print(sliceDstExternal2[0])
|
||||||
|
%sliceDstExternal2.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal2.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal2.val)
|
||||||
|
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @main.init() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
; equivalent of:
|
||||||
|
; uint8SliceDst = make([]uint8, len(uint8SliceSrc))
|
||||||
|
%uint8SliceSrc = load { ptr, i64, i64 }, ptr @main.uint8SliceSrc
|
||||||
|
%uint8SliceSrc.len = extractvalue { ptr, i64, i64 } %uint8SliceSrc, 1
|
||||||
|
%uint8SliceDst.buf = call ptr @runtime.alloc(i64 %uint8SliceSrc.len, ptr null)
|
||||||
|
%0 = insertvalue { ptr, i64, i64 } undef, ptr %uint8SliceDst.buf, 0
|
||||||
|
%1 = insertvalue { ptr, i64, i64 } %0, i64 %uint8SliceSrc.len, 1
|
||||||
|
%2 = insertvalue { ptr, i64, i64 } %1, i64 %uint8SliceSrc.len, 2
|
||||||
|
store { ptr, i64, i64 } %2, ptr @main.uint8SliceDst
|
||||||
|
|
||||||
|
; equivalent of:
|
||||||
|
; copy(uint8SliceDst, uint8SliceSrc)
|
||||||
|
%uint8SliceSrc.buf = extractvalue { ptr, i64, i64 } %uint8SliceSrc, 0
|
||||||
|
%copy.n = call i64 @runtime.sliceCopy(ptr %uint8SliceDst.buf, ptr %uint8SliceSrc.buf, i64 %uint8SliceSrc.len, i64 %uint8SliceSrc.len, i64 1)
|
||||||
|
|
||||||
|
; equivalent of:
|
||||||
|
; int16SliceDst = make([]int16, len(int16SliceSrc))
|
||||||
|
%int16SliceSrc = load { ptr, i64, i64 }, ptr @main.int16SliceSrc
|
||||||
|
%int16SliceSrc.len = extractvalue { ptr, i64, i64 } %int16SliceSrc, 1
|
||||||
|
%int16SliceSrc.len.bytes = mul i64 %int16SliceSrc.len, 2
|
||||||
|
%int16SliceDst.buf = call ptr @runtime.alloc(i64 %int16SliceSrc.len.bytes, ptr null)
|
||||||
|
%3 = insertvalue { ptr, i64, i64 } undef, ptr %int16SliceDst.buf, 0
|
||||||
|
%4 = insertvalue { ptr, i64, i64 } %3, i64 %int16SliceSrc.len, 1
|
||||||
|
%5 = insertvalue { ptr, i64, i64 } %4, i64 %int16SliceSrc.len, 2
|
||||||
|
store { ptr, i64, i64 } %5, ptr @main.int16SliceDst
|
||||||
|
|
||||||
|
; equivalent of:
|
||||||
|
; copy(int16SliceDst, int16SliceSrc)
|
||||||
|
%int16SliceSrc.buf = extractvalue { ptr, i64, i64 } %int16SliceSrc, 0
|
||||||
|
%copy.n2 = call i64 @runtime.sliceCopy(ptr %int16SliceDst.buf, ptr %int16SliceSrc.buf, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2)
|
||||||
|
|
||||||
|
; Copy slice that has a known value.
|
||||||
|
%copy.n3 = call i64 @runtime.sliceCopy(ptr @main.sliceDstUntaint.buf, ptr @main.sliceSrcUntaint.buf, i64 2, i64 2, i64 1)
|
||||||
|
|
||||||
|
; Copy slice that might have been modified by the external @use call.
|
||||||
|
; This is a fix for https://github.com/tinygo-org/tinygo/issues/3890.
|
||||||
|
call void @use(ptr @main.sliceSrcTaint.buf)
|
||||||
|
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
||||||
|
|
||||||
|
; Test that copying from or into external buffers works correctly.
|
||||||
|
; These copy operations must be done at runtime.
|
||||||
|
; https://github.com/tinygo-org/tinygo/issues/4895
|
||||||
|
%copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1)
|
||||||
|
%copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1)
|
||||||
|
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Vendored
+42
@@ -0,0 +1,42 @@
|
|||||||
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
|
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
||||||
|
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal1.buf = external global [2 x i8]
|
||||||
|
@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceDstExternal2.buf = external global [2 x i8]
|
||||||
|
|
||||||
|
declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr
|
||||||
|
|
||||||
|
declare void @runtime.printuint8(i8) local_unnamed_addr
|
||||||
|
|
||||||
|
declare void @runtime.printint16(i16) local_unnamed_addr
|
||||||
|
|
||||||
|
declare void @use(ptr) local_unnamed_addr
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @use(ptr @main.sliceSrcTaint.buf)
|
||||||
|
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
||||||
|
%copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1)
|
||||||
|
%copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @main() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @runtime.printuint8(i8 3)
|
||||||
|
call void @runtime.printuint8(i8 3)
|
||||||
|
call void @runtime.printint16(i16 5)
|
||||||
|
call void @runtime.printint16(i16 5)
|
||||||
|
call void @runtime.printuint8(i8 97)
|
||||||
|
%sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
||||||
|
%sliceDstExternal1.val = load i8, ptr @main.sliceDstExternal1.buf, align 1
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal1.val)
|
||||||
|
%sliceDstExternal2.val = load i8, ptr @main.sliceDstExternal2.buf, align 1
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal2.val)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
+1
-1
Submodule lib/bdwgc updated: 7577ca7c2d...1166f11f7d
@@ -1635,7 +1635,6 @@ func main() {
|
|||||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||||
monitor := flag.Bool("monitor", false, "enable serial monitor")
|
monitor := flag.Bool("monitor", false, "enable serial monitor")
|
||||||
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
|
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
|
||||||
gocompatibility := flag.Bool("go-compatibility", true, "enable to check for Go versions compatibility, you can also configure this by setting the TINYGO_GOCOMPATIBILITY environment variable")
|
|
||||||
|
|
||||||
// Internal flags, that are only intended for TinyGo development.
|
// Internal flags, that are only intended for TinyGo development.
|
||||||
printIR := flag.Bool("internal-printir", false, "print LLVM IR")
|
printIR := flag.Bool("internal-printir", false, "print LLVM IR")
|
||||||
@@ -1713,16 +1712,6 @@ func main() {
|
|||||||
ocdCommands = strings.Split(*ocdCommandsString, ",")
|
ocdCommands = strings.Split(*ocdCommandsString, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
val, ok := os.LookupEnv("TINYGO_GOCOMPATIBILITY")
|
|
||||||
if ok {
|
|
||||||
b, err := strconv.ParseBool(val)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "could not parse TINYGO_GOCOMPATIBILITY value %q: %v\n", val, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
*gocompatibility = b
|
|
||||||
}
|
|
||||||
|
|
||||||
options := &compileopts.Options{
|
options := &compileopts.Options{
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
@@ -1759,7 +1748,6 @@ func main() {
|
|||||||
Timeout: *timeout,
|
Timeout: *timeout,
|
||||||
WITPackage: witPackage,
|
WITPackage: witPackage,
|
||||||
WITWorld: witWorld,
|
WITWorld: witWorld,
|
||||||
GoCompatibility: *gocompatibility,
|
|
||||||
}
|
}
|
||||||
if *printCommands {
|
if *printCommands {
|
||||||
options.PrintCommands = printCommand
|
options.PrintCommands = printCommand
|
||||||
|
|||||||
@@ -222,7 +222,6 @@ func TestBuild(t *testing.T) {
|
|||||||
// to be sure.
|
// to be sure.
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
|
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
|
||||||
emuCheck(t, options)
|
|
||||||
runTest("cgo/", options, t, nil, nil)
|
runTest("cgo/", options, t, nil, nil)
|
||||||
})
|
})
|
||||||
} else if runtime.GOOS == "windows" {
|
} else if runtime.GOOS == "windows" {
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ func Asm(asm string)
|
|||||||
// recognizes template values in the form {name}, like so:
|
// recognizes template values in the form {name}, like so:
|
||||||
//
|
//
|
||||||
// arm.AsmFull(
|
// arm.AsmFull(
|
||||||
// "str {value}, [{result}]",
|
// "str {value}, {result}",
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "value": 1,
|
// "value": 1
|
||||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
// "result": &dest,
|
||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
// You can use {} in the asm string (which expands to a register) to set the
|
// You can use {} in the asm string (which expands to a register) to set the
|
||||||
|
|||||||
@@ -1,6 +1,29 @@
|
|||||||
.syntax unified
|
.syntax unified
|
||||||
.cfi_sections .debug_frame
|
.cfi_sections .debug_frame
|
||||||
|
|
||||||
|
.section .text.HardFault_Handler
|
||||||
|
.global HardFault_Handler
|
||||||
|
.type HardFault_Handler, %function
|
||||||
|
HardFault_Handler:
|
||||||
|
.cfi_startproc
|
||||||
|
// Put the old stack pointer in the first argument, for easy debugging. This
|
||||||
|
// is especially useful on Cortex-M0, which supports far fewer debug
|
||||||
|
// facilities.
|
||||||
|
mov r0, sp
|
||||||
|
|
||||||
|
// Load the default stack pointer from address 0 so that we can call normal
|
||||||
|
// functions again that expect a working stack. However, it will corrupt the
|
||||||
|
// old stack so the function below must not attempt to recover from this
|
||||||
|
// fault.
|
||||||
|
movs r3, #0
|
||||||
|
ldr r3, [r3]
|
||||||
|
mov sp, r3
|
||||||
|
|
||||||
|
// Continue handling this error in Go.
|
||||||
|
bl handleHardFault
|
||||||
|
.cfi_endproc
|
||||||
|
.size HardFault_Handler, .-HardFault_Handler
|
||||||
|
|
||||||
// This is a convenience function for semihosting support.
|
// This is a convenience function for semihosting support.
|
||||||
// At some point, this should be replaced by inline assembly.
|
// At some point, this should be replaced by inline assembly.
|
||||||
.section .text.SemihostingCall
|
.section .text.SemihostingCall
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ func Asm(asm string)
|
|||||||
// recognizes template values in the form {name}, like so:
|
// recognizes template values in the form {name}, like so:
|
||||||
//
|
//
|
||||||
// arm.AsmFull(
|
// arm.AsmFull(
|
||||||
// "str {value}, [{result}]",
|
// "str {value}, {result}",
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "value": 1,
|
// "value": 1
|
||||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
// "result": &dest,
|
||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
// You can use {} in the asm string (which expands to a register) to set the
|
// You can use {} in the asm string (which expands to a register) to set the
|
||||||
|
|||||||
+3
-3
@@ -10,10 +10,10 @@ func Asm(asm string)
|
|||||||
// recognizes template values in the form {name}, like so:
|
// recognizes template values in the form {name}, like so:
|
||||||
//
|
//
|
||||||
// arm.AsmFull(
|
// arm.AsmFull(
|
||||||
// "str {value}, [{result}]",
|
// "str {value}, {result}",
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "value": 1,
|
// "value": 1
|
||||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
// "result": &dest,
|
||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
// You can use {} in the asm string (which expands to a register) to set the
|
// You can use {} in the asm string (which expands to a register) to set the
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ func Asm(asm string)
|
|||||||
// recognizes template values in the form {name}, like so:
|
// recognizes template values in the form {name}, like so:
|
||||||
//
|
//
|
||||||
// arm.AsmFull(
|
// arm.AsmFull(
|
||||||
// "st {value}, [{result}]",
|
// "st {value}, {result}",
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "value": 1,
|
// "value": 1
|
||||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
// "result": &dest,
|
||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
// You can use {} in the asm string (which expands to a register) to set the
|
// You can use {} in the asm string (which expands to a register) to set the
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
//go:build digispark
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Use Timer1 for PWM (recommended for ATtiny85)
|
|
||||||
pwm = machine.Timer1
|
|
||||||
pinA = machine.P1 // PB1, Timer1 channel A (LED pin)
|
|
||||||
pinB = machine.P4 // PB4, Timer1 channel B
|
|
||||||
)
|
|
||||||
@@ -13,14 +13,12 @@ const (
|
|||||||
// 64-bit int => bits = 6
|
// 64-bit int => bits = 6
|
||||||
sizeBits = 4 + unsafe.Sizeof(uintptr(0))/4
|
sizeBits = 4 + unsafe.Sizeof(uintptr(0))/4
|
||||||
|
|
||||||
ptrAlign = unsafe.Alignof(uintptr(0))
|
|
||||||
|
|
||||||
sizeShift = sizeBits + 1
|
sizeShift = sizeBits + 1
|
||||||
|
|
||||||
NoPtrs = Layout((0 << sizeShift) | (1 << 1) | 1)
|
NoPtrs = Layout(uintptr(0b0<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||||
Pointer = Layout((1 << sizeShift) | ((unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1)
|
Pointer = Layout(uintptr(0b1<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||||
String = Layout((1 << sizeShift) | ((unsafe.Sizeof("") / ptrAlign) << 1) | 1)
|
String = Layout(uintptr(0b01<<sizeShift) | uintptr(0b10<<1) | uintptr(1))
|
||||||
Slice = Layout((1 << sizeShift) | ((unsafe.Sizeof([]byte{}) / ptrAlign) << 1) | 1)
|
Slice = Layout(uintptr(0b001<<sizeShift) | uintptr(0b11<<1) | uintptr(1))
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) }
|
func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) }
|
||||||
|
|||||||
@@ -86,64 +86,6 @@ func (v Value) Interface() interface{} {
|
|||||||
return valueInterfaceUnsafe(v)
|
return valueInterfaceUnsafe(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TypeAssert[T any](v Value) (T, bool) {
|
|
||||||
if v.typecode == nil {
|
|
||||||
panic("reflect.TypeAssert: zero Value")
|
|
||||||
}
|
|
||||||
if !v.isExported() {
|
|
||||||
// Do not allow access to unexported values via TypeAssert,
|
|
||||||
// because they might be pointers that should not be
|
|
||||||
// writable or methods or function that should not be callable.
|
|
||||||
panic("reflect.TypeAssert: cannot return value obtained from unexported field or method")
|
|
||||||
}
|
|
||||||
|
|
||||||
typ := TypeFor[T]()
|
|
||||||
|
|
||||||
// If v is an interface, return the element inside the interface.
|
|
||||||
//
|
|
||||||
// T is a concrete type and v is an interface. For example:
|
|
||||||
//
|
|
||||||
// var v any = int(1)
|
|
||||||
// val := ValueOf(&v).Elem()
|
|
||||||
// TypeAssert[int](val) == val.Interface().(int)
|
|
||||||
//
|
|
||||||
// T is a interface and v is a non-nil interface value. For example:
|
|
||||||
//
|
|
||||||
// var v any = &someError{}
|
|
||||||
// val := ValueOf(&v).Elem()
|
|
||||||
// TypeAssert[error](val) == val.Interface().(error)
|
|
||||||
//
|
|
||||||
// T is a interface and v is a nil interface value. For example:
|
|
||||||
//
|
|
||||||
// var v error = nil
|
|
||||||
// val := ValueOf(&v).Elem()
|
|
||||||
// TypeAssert[error](val) == val.Interface().(error)
|
|
||||||
if v.Kind() == Interface {
|
|
||||||
val, ok := valueInterfaceUnsafe(v).(T)
|
|
||||||
return val, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// If T is an interface and v is a concrete type. For example:
|
|
||||||
//
|
|
||||||
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any)
|
|
||||||
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
|
|
||||||
if typ.Kind() == Interface {
|
|
||||||
val, ok := valueInterfaceUnsafe(v).(T)
|
|
||||||
return val, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both v and T must be concrete types.
|
|
||||||
// The only way for an type-assertion to match is if the types are equal.
|
|
||||||
if typ != v.typecode {
|
|
||||||
var zero T
|
|
||||||
return zero, false
|
|
||||||
}
|
|
||||||
if !v.isIndirect() {
|
|
||||||
return *(*T)(unsafe.Pointer(&v.value)), true
|
|
||||||
}
|
|
||||||
return *(*T)(v.value), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
|
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
|
||||||
// be subject to the isExported check.
|
// be subject to the isExported check.
|
||||||
func valueInterfaceUnsafe(v Value) interface{} {
|
func valueInterfaceUnsafe(v Value) interface{} {
|
||||||
@@ -1796,9 +1738,6 @@ func (e *ValueError) Error() string {
|
|||||||
//go:linkname memcpy runtime.memcpy
|
//go:linkname memcpy runtime.memcpy
|
||||||
func memcpy(dst, src unsafe.Pointer, size uintptr)
|
func memcpy(dst, src unsafe.Pointer, size uintptr)
|
||||||
|
|
||||||
//go:linkname memmove runtime.memmove
|
|
||||||
func memmove(dst, src unsafe.Pointer, size uintptr)
|
|
||||||
|
|
||||||
//go:linkname memzero runtime.memzero
|
//go:linkname memzero runtime.memzero
|
||||||
func memzero(ptr unsafe.Pointer, size uintptr)
|
func memzero(ptr unsafe.Pointer, size uintptr)
|
||||||
|
|
||||||
@@ -1806,7 +1745,10 @@ func memzero(ptr unsafe.Pointer, size uintptr)
|
|||||||
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
|
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
//go:linkname sliceAppend runtime.sliceAppend
|
//go:linkname sliceAppend runtime.sliceAppend
|
||||||
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr)
|
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr)
|
||||||
|
|
||||||
|
//go:linkname sliceCopy runtime.sliceCopy
|
||||||
|
func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int
|
||||||
|
|
||||||
// Copy copies the contents of src into dst until either
|
// Copy copies the contents of src into dst until either
|
||||||
// dst has been filled or src has been exhausted.
|
// dst has been filled or src has been exhausted.
|
||||||
@@ -1837,10 +1779,7 @@ func Copy(dst, src Value) int {
|
|||||||
dst.checkRO()
|
dst.checkRO()
|
||||||
}
|
}
|
||||||
|
|
||||||
minLen := min(dstlen, srclen)
|
return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size())
|
||||||
elemSize := dst.typecode.elem().Size()
|
|
||||||
memmove(dstbuf, srcbuf, minLen*elemSize)
|
|
||||||
return int(minLen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buflen(v Value) (unsafe.Pointer, uintptr) {
|
func buflen(v Value) (unsafe.Pointer, uintptr) {
|
||||||
@@ -1871,7 +1810,7 @@ func buflen(v Value) (unsafe.Pointer, uintptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname sliceGrow runtime.sliceGrow
|
//go:linkname sliceGrow runtime.sliceGrow
|
||||||
func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr)
|
func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr)
|
||||||
|
|
||||||
// extend slice to hold n new elements
|
// extend slice to hold n new elements
|
||||||
func extendSlice(v Value, n int) sliceHeader {
|
func extendSlice(v Value, n int) sliceHeader {
|
||||||
@@ -1884,10 +1823,7 @@ func extendSlice(v Value, n int) sliceHeader {
|
|||||||
old = *(*sliceHeader)(v.value)
|
old = *(*sliceHeader)(v.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
elem := v.typecode.elem()
|
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size())
|
||||||
elemSize := elem.Size()
|
|
||||||
elemLayout := elem.gcLayout()
|
|
||||||
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), elemSize, elemLayout)
|
|
||||||
|
|
||||||
return sliceHeader{
|
return sliceHeader{
|
||||||
data: nbuf,
|
data: nbuf,
|
||||||
@@ -1926,10 +1862,8 @@ func AppendSlice(s, t Value) Value {
|
|||||||
}
|
}
|
||||||
sSlice := (*sliceHeader)(s.value)
|
sSlice := (*sliceHeader)(s.value)
|
||||||
tSlice := (*sliceHeader)(t.value)
|
tSlice := (*sliceHeader)(t.value)
|
||||||
elem := s.typecode.elem()
|
elemSize := s.typecode.elem().Size()
|
||||||
elemSize := elem.Size()
|
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize)
|
||||||
elemLayout := elem.gcLayout()
|
|
||||||
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize, elemLayout)
|
|
||||||
result := &sliceHeader{
|
result := &sliceHeader{
|
||||||
data: ptr,
|
data: ptr,
|
||||||
len: len,
|
len: len,
|
||||||
|
|||||||
@@ -108,10 +108,6 @@ func (*stackState) unwind()
|
|||||||
func (t *Task) Resume() {
|
func (t *Task) Resume() {
|
||||||
// The current task must be saved and restored because this can nest on WASM with JS.
|
// The current task must be saved and restored because this can nest on WASM with JS.
|
||||||
prevTask := currentTask
|
prevTask := currentTask
|
||||||
if prevTask == nil {
|
|
||||||
// Save the system stack pointer.
|
|
||||||
saveStackPointer()
|
|
||||||
}
|
|
||||||
t.gcData.swap()
|
t.gcData.swap()
|
||||||
currentTask = t
|
currentTask = t
|
||||||
if !t.state.launched {
|
if !t.state.launched {
|
||||||
@@ -127,9 +123,6 @@ func (t *Task) Resume() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname saveStackPointer runtime.saveStackPointer
|
|
||||||
func saveStackPointer()
|
|
||||||
|
|
||||||
//export tinygo_rewind
|
//export tinygo_rewind
|
||||||
func (*state) rewind()
|
func (*state) rewind()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build scheduler.tasks && (esp32 || esp32s3)
|
//go:build scheduler.tasks && esp32
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
|
|||||||
@@ -120,21 +120,15 @@ int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, u
|
|||||||
#endif
|
#endif
|
||||||
pthread_attr_t attrs;
|
pthread_attr_t attrs;
|
||||||
pthread_attr_init(&attrs);
|
pthread_attr_init(&attrs);
|
||||||
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
|
||||||
pthread_attr_setstacksize(&attrs, stackSize);
|
pthread_attr_setstacksize(&attrs, stackSize);
|
||||||
int result = pthread_create(thread, &attrs, &start_wrapper, &state);
|
int result = pthread_create(thread, &attrs, &start_wrapper, &state);
|
||||||
pthread_attr_destroy(&attrs);
|
pthread_attr_destroy(&attrs);
|
||||||
if (result != 0) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until the thread has been created and read all state_pass variables.
|
// Wait until the thread has been created and read all state_pass variables.
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
dispatch_semaphore_wait(state.startlock, DISPATCH_TIME_FOREVER);
|
dispatch_semaphore_wait(state.startlock, DISPATCH_TIME_FOREVER);
|
||||||
dispatch_release(state.startlock);
|
|
||||||
#else
|
#else
|
||||||
sem_wait(&state.startlock);
|
sem_wait(&state.startlock);
|
||||||
sem_destroy(&state.startlock);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -23,15 +23,16 @@ type state struct {
|
|||||||
// is needed to be able to scan the stack.
|
// is needed to be able to scan the stack.
|
||||||
stackTop uintptr
|
stackTop uintptr
|
||||||
|
|
||||||
// Lowest address of the stack.
|
|
||||||
// This is populated when the thread is stopped by the GC.
|
|
||||||
stackBottom uintptr
|
|
||||||
|
|
||||||
// Next task in the activeTasks queue.
|
// Next task in the activeTasks queue.
|
||||||
QueueNext *Task
|
QueueNext *Task
|
||||||
|
|
||||||
// Semaphore to pause/resume the thread atomically.
|
// Semaphore to pause/resume the thread atomically.
|
||||||
pauseSem Semaphore
|
pauseSem Semaphore
|
||||||
|
|
||||||
|
// Semaphore used for stack scanning.
|
||||||
|
// We can't reuse pauseSem here since the thread might have been paused for
|
||||||
|
// other reasons (for example, because it was waiting on a channel).
|
||||||
|
gcSem Semaphore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goroutine counter, starting at 0 for the main goroutine.
|
// Goroutine counter, starting at 0 for the main goroutine.
|
||||||
@@ -95,9 +96,6 @@ func (t *Task) Resume() {
|
|||||||
t.state.pauseSem.Post()
|
t.state.pauseSem.Post()
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherGoroutines is the total number of live goroutines minus one.
|
|
||||||
var otherGoroutines uint32
|
|
||||||
|
|
||||||
// Start a new OS thread.
|
// Start a new OS thread.
|
||||||
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
t := &Task{}
|
t := &Task{}
|
||||||
@@ -117,7 +115,6 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
|||||||
}
|
}
|
||||||
t.state.QueueNext = activeTasks
|
t.state.QueueNext = activeTasks
|
||||||
activeTasks = t
|
activeTasks = t
|
||||||
otherGoroutines++
|
|
||||||
activeTaskLock.Unlock()
|
activeTaskLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +135,6 @@ func taskExited(t *Task) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
otherGoroutines--
|
|
||||||
activeTaskLock.Unlock()
|
activeTaskLock.Unlock()
|
||||||
|
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
@@ -147,42 +143,9 @@ func taskExited(t *Task) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// scanWaitGroup is used to wait on until all threads have finished the current state transition.
|
// Futex to wait on until all tasks have finished scanning the stack.
|
||||||
var scanWaitGroup waitGroup
|
// This is basically a sync.WaitGroup.
|
||||||
|
var scanDoneFutex Futex
|
||||||
type waitGroup struct {
|
|
||||||
f Futex
|
|
||||||
}
|
|
||||||
|
|
||||||
func initWaitGroup(n uint32) waitGroup {
|
|
||||||
var wg waitGroup
|
|
||||||
wg.f.Store(n)
|
|
||||||
return wg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wg *waitGroup) done() {
|
|
||||||
if wg.f.Add(^uint32(0)) == 0 {
|
|
||||||
wg.f.WakeAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wg *waitGroup) wait() {
|
|
||||||
for {
|
|
||||||
val := wg.f.Load()
|
|
||||||
if val == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wg.f.Wait(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// gcState is used to track and notify threads when the GC is stopping/resuming.
|
|
||||||
var gcState Futex
|
|
||||||
|
|
||||||
const (
|
|
||||||
gcStateResumed = iota
|
|
||||||
gcStateStopped
|
|
||||||
)
|
|
||||||
|
|
||||||
// GC scan phase. Because we need to stop the world while scanning, this kinda
|
// GC scan phase. Because we need to stop the world while scanning, this kinda
|
||||||
// needs to be done in the tasks package.
|
// needs to be done in the tasks package.
|
||||||
@@ -192,71 +155,65 @@ const (
|
|||||||
func GCStopWorldAndScan() {
|
func GCStopWorldAndScan() {
|
||||||
current := Current()
|
current := Current()
|
||||||
|
|
||||||
// NOTE: This does not need to be atomic.
|
// Don't allow new goroutines to be started while pausing/resuming threads
|
||||||
if gcState.Load() == gcStateResumed {
|
// in the stop-the-world phase.
|
||||||
// Don't allow new goroutines to be started while pausing/resuming threads
|
activeTaskLock.Lock()
|
||||||
// in the stop-the-world phase.
|
|
||||||
activeTaskLock.Lock()
|
|
||||||
|
|
||||||
// Wait for threads to finish resuming.
|
// Pause all other threads.
|
||||||
scanWaitGroup.wait()
|
numOtherThreads := uint32(0)
|
||||||
|
|
||||||
// Change the gc state to stopped.
|
|
||||||
// NOTE: This does not need to be atomic.
|
|
||||||
gcState.Store(gcStateStopped)
|
|
||||||
|
|
||||||
// Set the number of threads to wait for.
|
|
||||||
scanWaitGroup = initWaitGroup(otherGoroutines)
|
|
||||||
|
|
||||||
// Pause all other threads.
|
|
||||||
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
|
||||||
if t != current {
|
|
||||||
tinygo_task_send_gc_signal(t.state.thread)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the threads to finish stopping.
|
|
||||||
scanWaitGroup.wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan other thread stacks.
|
|
||||||
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||||
if t != current {
|
if t != current {
|
||||||
markRoots(t.state.stackBottom, t.state.stackTop)
|
numOtherThreads++
|
||||||
|
tinygo_task_send_gc_signal(t.state.thread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store the number of threads to wait for in the futex.
|
||||||
|
// This is the equivalent of doing an initial wg.Add(numOtherThreads).
|
||||||
|
scanDoneFutex.Store(numOtherThreads)
|
||||||
|
|
||||||
// Scan the current stack, and all current registers.
|
// Scan the current stack, and all current registers.
|
||||||
scanCurrentStack()
|
scanCurrentStack()
|
||||||
|
|
||||||
|
// Wake each paused thread for the first time so it will scan the stack.
|
||||||
|
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||||
|
if t != current {
|
||||||
|
t.state.gcSem.Post()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until all threads have finished scanning their stack.
|
||||||
|
// This is the equivalent of wg.Wait()
|
||||||
|
for {
|
||||||
|
val := scanDoneFutex.Load()
|
||||||
|
if val == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
scanDoneFutex.Wait(val)
|
||||||
|
}
|
||||||
|
|
||||||
// Scan all globals (implemented in the runtime).
|
// Scan all globals (implemented in the runtime).
|
||||||
gcScanGlobals()
|
gcScanGlobals()
|
||||||
}
|
}
|
||||||
|
|
||||||
// After the GC is done scanning, resume all other threads.
|
// After the GC is done scanning, resume all other threads.
|
||||||
|
//
|
||||||
|
// This must only be called after a GCStopWorldAndScan call.
|
||||||
func GCResumeWorld() {
|
func GCResumeWorld() {
|
||||||
// NOTE: This does not need to be atomic.
|
current := Current()
|
||||||
if gcState.Load() == gcStateResumed {
|
|
||||||
// This is already resumed.
|
// Wake each paused thread for the second time, so they will resume normal
|
||||||
return
|
// operation.
|
||||||
|
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||||
|
if t != current {
|
||||||
|
t.state.gcSem.Post()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the wait group to track resume progress.
|
|
||||||
scanWaitGroup = initWaitGroup(otherGoroutines)
|
|
||||||
|
|
||||||
// Set the state to resumed.
|
|
||||||
gcState.Store(gcStateResumed)
|
|
||||||
|
|
||||||
// Wake all of the stopped threads.
|
|
||||||
gcState.WakeAll()
|
|
||||||
|
|
||||||
// Allow goroutines to start and exit again.
|
// Allow goroutines to start and exit again.
|
||||||
activeTaskLock.Unlock()
|
activeTaskLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname markRoots runtime.markRoots
|
|
||||||
func markRoots(start, end uintptr)
|
|
||||||
|
|
||||||
// Scan globals, implemented in the runtime package.
|
// Scan globals, implemented in the runtime package.
|
||||||
func gcScanGlobals()
|
func gcScanGlobals()
|
||||||
|
|
||||||
@@ -264,27 +221,34 @@ var stackScanLock PMutex
|
|||||||
|
|
||||||
//export tinygo_task_gc_pause
|
//export tinygo_task_gc_pause
|
||||||
func tingyo_task_gc_pause(sig int32) {
|
func tingyo_task_gc_pause(sig int32) {
|
||||||
// Write the entrty stack pointer to the state.
|
// Wait until we get the signal to start scanning the stack.
|
||||||
Current().state.stackBottom = uintptr(stacksave())
|
Current().state.gcSem.Wait()
|
||||||
|
|
||||||
// Notify the GC that we are stopped.
|
// Scan the thread stack.
|
||||||
scanWaitGroup.done()
|
// Only scan a single thread stack at a time, because the GC marking phase
|
||||||
|
// doesn't support parallelism.
|
||||||
|
// TODO: it may be possible to call markRoots directly (without saving
|
||||||
|
// registers) since we are in a signal handler that already saved a bunch of
|
||||||
|
// registers. This is an optimization left for a future time.
|
||||||
|
stackScanLock.Lock()
|
||||||
|
scanCurrentStack()
|
||||||
|
stackScanLock.Unlock()
|
||||||
|
|
||||||
// Wait for the GC to resume.
|
// Equivalent of wg.Done(): subtract one from the futex and if the result is
|
||||||
for gcState.Load() == gcStateStopped {
|
// 0 (meaning we were the last in the waitgroup), wake the waiting thread.
|
||||||
gcState.Wait(gcStateStopped)
|
n := uint32(1)
|
||||||
|
if scanDoneFutex.Add(-n) == 0 {
|
||||||
|
scanDoneFutex.Wake()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the GC that we have resumed.
|
// Wait until we get the signal we can resume normally (after the mark phase
|
||||||
scanWaitGroup.done()
|
// has finished).
|
||||||
|
Current().state.gcSem.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export tinygo_scanCurrentStack
|
//go:export tinygo_scanCurrentStack
|
||||||
func scanCurrentStack()
|
func scanCurrentStack()
|
||||||
|
|
||||||
//go:linkname stacksave runtime.stacksave
|
|
||||||
func stacksave() unsafe.Pointer
|
|
||||||
|
|
||||||
// Return the highest address of the current stack.
|
// Return the highest address of the current stack.
|
||||||
func StackTop() uintptr {
|
func StackTop() uintptr {
|
||||||
return Current().state.stackTop
|
return Current().state.stackTop
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
//go:build amken_trio
|
|
||||||
|
|
||||||
// RabbitPNP Toolhead Board
|
|
||||||
// MCU: STM32G0B1CBTx (LQFP48, 128KB Flash, 144KB RAM)
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/stm32"
|
|
||||||
"runtime/interrupt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Vacuum sensors (PWM input via TIM2)
|
|
||||||
const (
|
|
||||||
VAC1 = PA0 // TIM2_CH1
|
|
||||||
VAC2 = PA1 // TIM2_CH2
|
|
||||||
)
|
|
||||||
|
|
||||||
// Motor 1 pins (stepper driver)
|
|
||||||
const (
|
|
||||||
M1_CS = PC7
|
|
||||||
M1_DIR = PB13 // REFL
|
|
||||||
M1_STEP = PB14 // REFR
|
|
||||||
M1_ENN = PA10 // Enable (active low)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Motor 2 pins (stepper driver)
|
|
||||||
const (
|
|
||||||
M2_CS = PA9
|
|
||||||
M2_DIR = PB12 // REFL
|
|
||||||
M2_STEP = PB11 // REFR
|
|
||||||
M2_ENN = PC6 // Enable (active low)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Motor 3 pins (stepper driver)
|
|
||||||
const (
|
|
||||||
M3_CS = PB15
|
|
||||||
M3_DIR = PB2 // REFL
|
|
||||||
M3_STEP = PB1 // REFR
|
|
||||||
M3_ENN = PA8 // Enable (active low)
|
|
||||||
)
|
|
||||||
|
|
||||||
// LED
|
|
||||||
const (
|
|
||||||
LED = LED1
|
|
||||||
LED_BUILTIN = LED1
|
|
||||||
LED1 = PB7
|
|
||||||
)
|
|
||||||
|
|
||||||
// Solenoid and Neopixel (PWM via TIM4)
|
|
||||||
const (
|
|
||||||
SOLENOID = PB8 // TIM4_CH3
|
|
||||||
NEOPIXEL = PB9 // TIM4_CH4
|
|
||||||
)
|
|
||||||
|
|
||||||
// Endstops
|
|
||||||
const (
|
|
||||||
ENDSTOP_IN1 = PC13
|
|
||||||
ENDSTOP_IN2 = PC14
|
|
||||||
)
|
|
||||||
|
|
||||||
// Magnetic sensor
|
|
||||||
const (
|
|
||||||
MAG1 = PB3
|
|
||||||
)
|
|
||||||
|
|
||||||
// Accelerometer chip select (LIS2D on SPI2)
|
|
||||||
const (
|
|
||||||
LIS2D_CS = PB5
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI1 pins (motor drivers)
|
|
||||||
const (
|
|
||||||
SPI1_SCK_PIN = PA5
|
|
||||||
SPI1_SDO_PIN = PA2 // MOSI
|
|
||||||
SPI1_SDI_PIN = PA6 // MISO
|
|
||||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
|
||||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
|
||||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI2 pins (accelerometer)
|
|
||||||
const (
|
|
||||||
SPI2_SCK_PIN = PB10
|
|
||||||
SPI2_SDO_PIN = PA4 // MOSI
|
|
||||||
SPI2_SDI_PIN = PA3 // MISO
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2C2 pins
|
|
||||||
const (
|
|
||||||
I2C2_SCL_PIN = PA7
|
|
||||||
I2C2_SDA_PIN = PB4
|
|
||||||
I2C0_SCL_PIN = I2C2_SCL_PIN
|
|
||||||
I2C0_SDA_PIN = I2C2_SDA_PIN
|
|
||||||
)
|
|
||||||
|
|
||||||
// FDCAN1 pins
|
|
||||||
const (
|
|
||||||
CAN_RX = PD0
|
|
||||||
CAN_TX = PD1
|
|
||||||
)
|
|
||||||
|
|
||||||
// USB pins
|
|
||||||
const (
|
|
||||||
USB_DM = PA11
|
|
||||||
USB_DP = PA12
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART pins (not directly connected but required by machine package)
|
|
||||||
const (
|
|
||||||
UART_TX_PIN = NoPin
|
|
||||||
UART_RX_PIN = NoPin
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// SPI1 for motor drivers
|
|
||||||
SPI1 = &SPI{
|
|
||||||
Bus: stm32.SPI1,
|
|
||||||
AltFuncSelector: AF0_SYSTEM,
|
|
||||||
}
|
|
||||||
SPI0 = SPI1
|
|
||||||
|
|
||||||
// SPI2 for accelerometer
|
|
||||||
SPI2 = &SPI{
|
|
||||||
Bus: stm32.SPI2,
|
|
||||||
AltFuncSelector: AF1_TIM1_TIM2_TIM3_LPTIM1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2C2
|
|
||||||
I2C2 = &I2C{
|
|
||||||
Bus: stm32.I2C2,
|
|
||||||
AltFuncSelector: AF6_SPI2_USART3_USART4_I2C1,
|
|
||||||
}
|
|
||||||
I2C0 = I2C2
|
|
||||||
|
|
||||||
// FDCAN1 on PD0 (RX) / PD1 (TX) with onboard transceiver
|
|
||||||
CAN1 = &_CAN1
|
|
||||||
_CAN1 = FDCAN{
|
|
||||||
Bus: stm32.FDCAN1,
|
|
||||||
TxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
|
||||||
RxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
|
||||||
instance: 0,
|
|
||||||
}
|
|
||||||
// Alias for convenience
|
|
||||||
CAN0 = CAN1
|
|
||||||
)
|
|
||||||
|
|
||||||
// Suppress unused import warning for interrupt package
|
|
||||||
var _ = interrupt.New
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// No UART configured on this board - uses USB or CAN for communication
|
|
||||||
}
|
|
||||||
@@ -2,26 +2,17 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
// Digispark is a tiny ATtiny85-based board with 6 I/O pins.
|
|
||||||
//
|
|
||||||
// PWM is available on the following pins:
|
|
||||||
// - P0 (PB0): Timer0 channel A
|
|
||||||
// - P1 (PB1): Timer0 channel B or Timer1 channel A (LED pin)
|
|
||||||
// - P4 (PB4): Timer1 channel B
|
|
||||||
//
|
|
||||||
// Timer1 is recommended for PWM as it provides more flexible frequency control.
|
|
||||||
|
|
||||||
// Return the current CPU frequency in hertz.
|
// Return the current CPU frequency in hertz.
|
||||||
func CPUFrequency() uint32 {
|
func CPUFrequency() uint32 {
|
||||||
return 16000000
|
return 16000000
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
P0 Pin = PB0 // PWM available (Timer0 OC0A)
|
P0 Pin = PB0
|
||||||
P1 Pin = PB1 // PWM available (Timer0 OC0B or Timer1 OC1A)
|
P1 Pin = PB1
|
||||||
P2 Pin = PB2
|
P2 Pin = PB2
|
||||||
P3 Pin = PB3
|
P3 Pin = PB3
|
||||||
P4 Pin = PB4 // PWM available (Timer1 OC1B)
|
P4 Pin = PB4
|
||||||
P5 Pin = PB5
|
P5 Pin = PB5
|
||||||
|
|
||||||
LED = P1
|
LED = P1
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
//go:build esp32s3_wroom1
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
const (
|
|
||||||
SPI1_SCK_PIN = GPIO12 // SCK
|
|
||||||
SPI1_MOSI_PIN = GPIO11 // SDO (MOSI)
|
|
||||||
SPI1_MISO_PIN = GPIO13 // SDI (MISO)
|
|
||||||
SPI1_CS_PIN = GPIO10 // CS
|
|
||||||
|
|
||||||
SPI2_SCK_PIN = GPIO36 // SCK
|
|
||||||
SPI2_MOSI_PIN = GPIO35 // SDO (MOSI)
|
|
||||||
SPI2_MISO_PIN = GPIO37 // SDI (MISO)
|
|
||||||
SPI2_CS_PIN = GPIO34 // CS
|
|
||||||
)
|
|
||||||
@@ -43,15 +43,6 @@ const (
|
|||||||
USBCDC_DP_PIN = PA25
|
USBCDC_DP_PIN = PA25
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART0 pins
|
|
||||||
const (
|
|
||||||
UART0_TX_PIN = D1
|
|
||||||
UART0_RX_PIN = D0
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART0 on the Feather M0.
|
|
||||||
var UART0 = &sercomUSART0
|
|
||||||
|
|
||||||
// UART1 pins
|
// UART1 pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = D10
|
UART_TX_PIN = D10
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
//go:build nucleog0b1re
|
|
||||||
|
|
||||||
// Schematic: https://www.st.com/resource/en/user_manual/um2324-stm32-nucleo64-boards-mb1360-stmicroelectronics.pdf
|
|
||||||
// Datasheet: https://www.st.com/resource/en/datasheet/stm32g0b1re.pdf
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/stm32"
|
|
||||||
"runtime/interrupt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Arduino Pins
|
|
||||||
A0 = PA0
|
|
||||||
A1 = PA1
|
|
||||||
A2 = PA4
|
|
||||||
A3 = PB1
|
|
||||||
A4 = PA11
|
|
||||||
A5 = PA12
|
|
||||||
|
|
||||||
D0 = PB7
|
|
||||||
D1 = PB6
|
|
||||||
D2 = PA10
|
|
||||||
D3 = PB3
|
|
||||||
D4 = PB5
|
|
||||||
D5 = PB4
|
|
||||||
D6 = PB10
|
|
||||||
D7 = PA8
|
|
||||||
D8 = PA9
|
|
||||||
D9 = PC7
|
|
||||||
D10 = PB0
|
|
||||||
D11 = PA7
|
|
||||||
D12 = PA6
|
|
||||||
D13 = PA5
|
|
||||||
D14 = PB9
|
|
||||||
D15 = PB8
|
|
||||||
)
|
|
||||||
|
|
||||||
// User LD4: the green LED is a user LED connected to ARDUINO signal D13 corresponding
|
|
||||||
// to STM32 I/O PA5.
|
|
||||||
const (
|
|
||||||
LED = LED_BUILTIN
|
|
||||||
LED_BUILTIN = LED_GREEN
|
|
||||||
LED_GREEN = PA5
|
|
||||||
)
|
|
||||||
|
|
||||||
// User B1: the user button is connected to PC13.
|
|
||||||
const (
|
|
||||||
BUTTON = PC13
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// UART pins
|
|
||||||
// PA2 and PA3 are connected to the ST-Link Virtual Com Port (VCP)
|
|
||||||
UART_TX_PIN = PA2
|
|
||||||
UART_RX_PIN = PA3
|
|
||||||
|
|
||||||
// I2C pins
|
|
||||||
// PB8 is SCL (connected to Arduino connector D15)
|
|
||||||
// PB9 is SDA (connected to Arduino connector D14)
|
|
||||||
I2C0_SCL_PIN = PB8
|
|
||||||
I2C0_SDA_PIN = PB9
|
|
||||||
|
|
||||||
// SPI pins
|
|
||||||
SPI1_SCK_PIN = PA5
|
|
||||||
SPI1_SDI_PIN = PA6
|
|
||||||
SPI1_SDO_PIN = PA7
|
|
||||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
|
||||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
|
||||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
|
||||||
|
|
||||||
// CAN pins (directly accessible on Nucleo-G0B1RE board)
|
|
||||||
// FDCAN1: PA11 (TX) / PA12 (RX) using AF9
|
|
||||||
// FDCAN2: PD12 (TX) / PD13 (RX) using AF3
|
|
||||||
CAN1_TX_PIN = PA11
|
|
||||||
CAN1_RX_PIN = PA12
|
|
||||||
CAN2_TX_PIN = PD12
|
|
||||||
CAN2_RX_PIN = PD13
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// USART2 is the hardware serial port connected to the onboard ST-LINK
|
|
||||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
|
||||||
UART1 = &_UART1
|
|
||||||
_UART1 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: stm32.USART2,
|
|
||||||
TxAltFuncSelector: AF1_TIM1_TIM2_TIM3_LPTIM1,
|
|
||||||
RxAltFuncSelector: AF1_TIM1_TIM2_TIM3_LPTIM1,
|
|
||||||
}
|
|
||||||
DefaultUART = UART1
|
|
||||||
|
|
||||||
// I2C1 is documented, alias to I2C0 as well
|
|
||||||
I2C1 = &I2C{
|
|
||||||
Bus: stm32.I2C1,
|
|
||||||
AltFuncSelector: AF6_SPI2_USART3_USART4_I2C1,
|
|
||||||
}
|
|
||||||
I2C0 = I2C1
|
|
||||||
|
|
||||||
// SPI1 is documented, alias to SPI0 as well
|
|
||||||
SPI1 = &SPI{
|
|
||||||
Bus: stm32.SPI1,
|
|
||||||
AltFuncSelector: AF0_SYSTEM,
|
|
||||||
}
|
|
||||||
SPI0 = SPI1
|
|
||||||
|
|
||||||
// FDCAN1 on PA11 (TX) / PA12 (RX)
|
|
||||||
CAN1 = &_CAN1
|
|
||||||
_CAN1 = FDCAN{
|
|
||||||
Bus: stm32.FDCAN1,
|
|
||||||
TxAltFuncSelect: AF9_FDCAN1_FDCAN2,
|
|
||||||
RxAltFuncSelect: AF9_FDCAN1_FDCAN2,
|
|
||||||
instance: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// FDCAN2 on PD12 (TX) / PD13 (RX)
|
|
||||||
CAN2 = &_CAN2
|
|
||||||
_CAN2 = FDCAN{
|
|
||||||
Bus: stm32.FDCAN2,
|
|
||||||
TxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
|
||||||
RxAltFuncSelect: AF3_FDCAN1_FDCAN2,
|
|
||||||
instance: 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
UART1.Interrupt = interrupt.New(stm32.IRQ_USART2_LPUART2, _UART1.handleInterrupt)
|
|
||||||
// Note: FDCAN interrupts share with USB (IRQ_UCPD1_UCPD2_USB = 8)
|
|
||||||
// User should configure interrupts via SetInterrupt method if needed
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
//go:build pico2_ice
|
|
||||||
|
|
||||||
// Most of the info is from
|
|
||||||
// https://pico2-ice.tinyvision.ai/md_pinout.html although
|
|
||||||
// (2025-09-07) RP4 appears twice in that pinout - the schematic is
|
|
||||||
// more clear. Consistent with other RPi boards, we use GPn instead of
|
|
||||||
// RPn to reference the RPi connected pins.
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// GPIO pins
|
|
||||||
const (
|
|
||||||
GP0 = GPIO0
|
|
||||||
GP1 = GPIO1
|
|
||||||
GP2 = GPIO2
|
|
||||||
GP3 = GPIO3
|
|
||||||
GP4 = GPIO4
|
|
||||||
GP5 = GPIO5
|
|
||||||
GP6 = GPIO6
|
|
||||||
GP7 = GPIO7
|
|
||||||
GP8 = GPIO8
|
|
||||||
GP9 = GPIO9
|
|
||||||
GP10 = GPIO10
|
|
||||||
GP11 = GPIO11
|
|
||||||
GP12 = GPIO12
|
|
||||||
GP13 = GPIO13
|
|
||||||
GP14 = GPIO14
|
|
||||||
GP15 = GPIO15
|
|
||||||
GP16 = GPIO16
|
|
||||||
GP17 = GPIO17
|
|
||||||
GP18 = GPIO18
|
|
||||||
GP19 = GPIO19
|
|
||||||
GP20 = GPIO20
|
|
||||||
GP21 = GPIO21
|
|
||||||
GP22 = GPIO22
|
|
||||||
GP23 = GPIO23
|
|
||||||
GP24 = GPIO24
|
|
||||||
GP25 = GPIO25
|
|
||||||
GP26 = GPIO26
|
|
||||||
GP27 = GPIO27
|
|
||||||
GP28 = GPIO28
|
|
||||||
GP29 = GPIO29
|
|
||||||
GP30 = GPIO30
|
|
||||||
GP31 = GPIO31
|
|
||||||
GP32 = GPIO32
|
|
||||||
GP33 = GPIO33
|
|
||||||
GP34 = GPIO34
|
|
||||||
GP35 = GPIO35
|
|
||||||
GP36 = GPIO36
|
|
||||||
GP37 = GPIO37
|
|
||||||
GP38 = GPIO38
|
|
||||||
GP39 = GPIO39
|
|
||||||
GP40 = GPIO40
|
|
||||||
GP41 = GPIO41
|
|
||||||
GP42 = GPIO42
|
|
||||||
GP43 = GPIO43
|
|
||||||
GP44 = GPIO44
|
|
||||||
GP45 = GPIO45
|
|
||||||
GP46 = GPIO46
|
|
||||||
GP47 = GPIO47
|
|
||||||
|
|
||||||
// RPi pins shared with ICE. The ICE number is what appears on
|
|
||||||
// the board silkscreen.
|
|
||||||
ICE9 = GP28
|
|
||||||
ICE11 = GP29
|
|
||||||
ICE14 = GP7
|
|
||||||
ICE15 = GP6
|
|
||||||
ICE16 = GP5
|
|
||||||
ICE17 = GP4
|
|
||||||
ICE18 = GP27
|
|
||||||
ICE19 = GP23
|
|
||||||
ICE20 = GP22
|
|
||||||
ICE21 = GP26
|
|
||||||
ICE23 = GP25
|
|
||||||
ICE25 = GP30
|
|
||||||
ICE26 = GP24
|
|
||||||
ICE27 = GP20
|
|
||||||
|
|
||||||
// FPGA Clock pin.
|
|
||||||
ICE35_G0 = GP21
|
|
||||||
|
|
||||||
// Silkscreen & Pinout names
|
|
||||||
ICE_SSN = ICE16
|
|
||||||
ICE_SO = ICE14
|
|
||||||
ICE_SI = ICE17
|
|
||||||
ICE_CK = ICE15
|
|
||||||
SD = GP2
|
|
||||||
SC = GP3
|
|
||||||
|
|
||||||
FPGA_RSTN = GP31
|
|
||||||
A3 = GP32
|
|
||||||
A1 = GP33
|
|
||||||
A4 = GP34
|
|
||||||
A2 = GP35
|
|
||||||
B3 = GP36
|
|
||||||
B1 = GP37
|
|
||||||
B4 = GP38
|
|
||||||
B2 = GP39
|
|
||||||
N0 = GP40 // On the board these are labeled "~0"
|
|
||||||
N1 = GP41
|
|
||||||
N2 = GP42
|
|
||||||
N3 = GP43
|
|
||||||
N4 = GP44
|
|
||||||
N5 = GP45
|
|
||||||
N6 = GP46
|
|
||||||
|
|
||||||
// Functions from Schematic.
|
|
||||||
ICE_DONE = GP40
|
|
||||||
USB_BOOT = GP42
|
|
||||||
|
|
||||||
// Button
|
|
||||||
SW1 = GP42
|
|
||||||
BOOTSEL = GP42
|
|
||||||
|
|
||||||
// Tricolor LEDs
|
|
||||||
LED_RED = GP1
|
|
||||||
LED_GREEN = GP0
|
|
||||||
LED_BLUE = GP9
|
|
||||||
|
|
||||||
// Onboard LED
|
|
||||||
LED = LED_GREEN
|
|
||||||
|
|
||||||
// Onboard crystal oscillator frequency, in MHz.
|
|
||||||
xoscFreq = 12 // MHz
|
|
||||||
)
|
|
||||||
|
|
||||||
// This board does not define default i2c pins.
|
|
||||||
const (
|
|
||||||
I2C0_SDA_PIN = NoPin
|
|
||||||
I2C0_SCL_PIN = NoPin
|
|
||||||
I2C1_SDA_PIN = NoPin
|
|
||||||
I2C1_SCL_PIN = NoPin
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI default pins
|
|
||||||
const (
|
|
||||||
// Default Serial Clock Bus 0 for SPI communications
|
|
||||||
SPI0_SCK_PIN = GPIO18
|
|
||||||
// Default Serial Out Bus 0 for SPI communications
|
|
||||||
SPI0_SDO_PIN = GPIO19 // Tx
|
|
||||||
// Default Serial In Bus 0 for SPI communications
|
|
||||||
SPI0_SDI_PIN = GPIO16 // Rx
|
|
||||||
|
|
||||||
// Default Serial Clock Bus 1 for SPI communications
|
|
||||||
SPI1_SCK_PIN = GPIO10
|
|
||||||
// Default Serial Out Bus 1 for SPI communications
|
|
||||||
SPI1_SDO_PIN = GPIO11 // Tx
|
|
||||||
// Default Serial In Bus 1 for SPI communications
|
|
||||||
SPI1_SDI_PIN = GPIO12 // Rx
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART pins
|
|
||||||
const (
|
|
||||||
UART0_TX_PIN = GPIO0
|
|
||||||
UART0_RX_PIN = GPIO1
|
|
||||||
UART1_TX_PIN = GPIO8
|
|
||||||
UART1_RX_PIN = GPIO9
|
|
||||||
UART_TX_PIN = UART0_TX_PIN
|
|
||||||
UART_RX_PIN = UART0_RX_PIN
|
|
||||||
)
|
|
||||||
|
|
||||||
var DefaultUART = UART0
|
|
||||||
|
|
||||||
// USB identifiers
|
|
||||||
const (
|
|
||||||
usb_STRING_PRODUCT = "Pico2"
|
|
||||||
usb_STRING_MANUFACTURER = "Raspberry Pi"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
usb_VID uint16 = 0x2E8A
|
|
||||||
usb_PID uint16 = 0x000A
|
|
||||||
)
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
//go:build vicharak_shrike_lite
|
|
||||||
|
|
||||||
// Pin mappings for Vicharak Shrike-Lite.
|
|
||||||
//
|
|
||||||
// Reference: https://vicharak-in.github.io/shrike/shrike_pinouts.html
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// Digital
|
|
||||||
const (
|
|
||||||
IO0 Pin = GPIO0
|
|
||||||
IO1 Pin = GPIO1
|
|
||||||
IO2 Pin = GPIO2
|
|
||||||
IO3 Pin = GPIO3
|
|
||||||
IO4 Pin = GPIO4
|
|
||||||
IO5 Pin = GPIO5
|
|
||||||
IO6 Pin = GPIO6
|
|
||||||
IO7 Pin = GPIO7
|
|
||||||
IO8 Pin = GPIO8
|
|
||||||
IO9 Pin = GPIO9
|
|
||||||
IO10 Pin = GPIO10
|
|
||||||
IO11 Pin = GPIO11
|
|
||||||
IO12 Pin = GPIO12
|
|
||||||
IO13 Pin = GPIO13
|
|
||||||
IO14 Pin = GPIO14
|
|
||||||
IO15 Pin = GPIO15
|
|
||||||
IO16 Pin = GPIO16
|
|
||||||
IO17 Pin = GPIO17
|
|
||||||
IO18 Pin = GPIO18
|
|
||||||
IO19 Pin = GPIO19
|
|
||||||
IO20 Pin = GPIO20
|
|
||||||
IO21 Pin = GPIO21
|
|
||||||
IO22 Pin = GPIO22
|
|
||||||
IO23 Pin = GPIO23
|
|
||||||
IO24 Pin = GPIO24
|
|
||||||
IO25 Pin = GPIO25
|
|
||||||
IO26 Pin = GPIO26
|
|
||||||
IO27 Pin = GPIO27
|
|
||||||
IO28 Pin = GPIO28
|
|
||||||
IO29 Pin = GPIO29
|
|
||||||
)
|
|
||||||
|
|
||||||
// FPGA Pins
|
|
||||||
const (
|
|
||||||
FPGA_EN Pin = IO13
|
|
||||||
FPGA_PWR Pin = IO12
|
|
||||||
// SPI_SCLK
|
|
||||||
F3 Pin = IO2
|
|
||||||
// SPI_SS
|
|
||||||
F4 Pin = IO1
|
|
||||||
// SPI_SI (MOSI)
|
|
||||||
F5 Pin = IO3
|
|
||||||
// SPI_SO (MISO) / CONFIG
|
|
||||||
F6 Pin = IO0
|
|
||||||
F18 Pin = IO14
|
|
||||||
F17 Pin = IO15
|
|
||||||
)
|
|
||||||
|
|
||||||
// Analog pins
|
|
||||||
const (
|
|
||||||
A0 Pin = IO26
|
|
||||||
A1 Pin = IO27
|
|
||||||
A2 Pin = IO28
|
|
||||||
A3 Pin = IO29
|
|
||||||
)
|
|
||||||
|
|
||||||
// LED
|
|
||||||
const (
|
|
||||||
LED = IO4
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2C pins
|
|
||||||
const (
|
|
||||||
I2C0_SDA_PIN Pin = IO24
|
|
||||||
I2C0_SCL_PIN Pin = IO25
|
|
||||||
|
|
||||||
I2C1_SDA_PIN Pin = IO6
|
|
||||||
I2C1_SCL_PIN Pin = IO7
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI pins
|
|
||||||
const (
|
|
||||||
SPI0_SCK_PIN Pin = IO18
|
|
||||||
SPI0_SDO_PIN Pin = IO19
|
|
||||||
SPI0_SDI_PIN Pin = IO20
|
|
||||||
|
|
||||||
SPI1_SCK_PIN Pin = IO10
|
|
||||||
SPI1_SDO_PIN Pin = IO11
|
|
||||||
SPI1_SDI_PIN Pin = IO8
|
|
||||||
)
|
|
||||||
|
|
||||||
// Onboard crystal oscillator frequency, in MHz.
|
|
||||||
const (
|
|
||||||
xoscFreq = 12 // MHz
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART pins
|
|
||||||
const (
|
|
||||||
UART0_TX_PIN = IO28
|
|
||||||
UART0_RX_PIN = IO29
|
|
||||||
UART_TX_PIN = UART0_TX_PIN
|
|
||||||
UART_RX_PIN = UART0_RX_PIN
|
|
||||||
UART1_TX_PIN = IO24
|
|
||||||
UART1_RX_PIN = IO25
|
|
||||||
)
|
|
||||||
|
|
||||||
var DefaultUART = UART0
|
|
||||||
|
|
||||||
// USB CDC identifiers
|
|
||||||
const (
|
|
||||||
usb_STRING_PRODUCT = "Shrike-Lite"
|
|
||||||
usb_STRING_MANUFACTURER = "Vicharak"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
usb_VID uint16 = 0x2e8a
|
|
||||||
usb_PID uint16 = 0x0003
|
|
||||||
)
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
//go:build xiao_esp32s3
|
|
||||||
|
|
||||||
// This file contains the pin mappings for the Seeed XIAO ESP32S3 boards.
|
|
||||||
//
|
|
||||||
// Seeed Studio XIAO ESP32S3 is an IoT mini development board based on
|
|
||||||
// the Espressif ESP32-S3 WiFi/Bluetooth dual-mode chip.
|
|
||||||
//
|
|
||||||
// - https://www.seeedstudio.com/XIAO-ESP32S3-p-5627.html
|
|
||||||
// - https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// Digital Pins
|
|
||||||
const (
|
|
||||||
D0 = GPIO1
|
|
||||||
D1 = GPIO2
|
|
||||||
D2 = GPIO3
|
|
||||||
D3 = GPIO4
|
|
||||||
D4 = GPIO5
|
|
||||||
D5 = GPIO6
|
|
||||||
D6 = GPIO43
|
|
||||||
D7 = GPIO44
|
|
||||||
D8 = GPIO7
|
|
||||||
D9 = GPIO8
|
|
||||||
D10 = GPIO9
|
|
||||||
)
|
|
||||||
|
|
||||||
// Analog pins
|
|
||||||
const (
|
|
||||||
A0 = GPIO1
|
|
||||||
A1 = GPIO2
|
|
||||||
A2 = GPIO3
|
|
||||||
A3 = GPIO4
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART pins
|
|
||||||
const (
|
|
||||||
UART_RX_PIN = GPIO44
|
|
||||||
UART_TX_PIN = GPIO43
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2C pins
|
|
||||||
const (
|
|
||||||
SDA_PIN = GPIO5
|
|
||||||
SCL_PIN = GPIO6
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI pins
|
|
||||||
const (
|
|
||||||
SPI1_SCK_PIN = GPIO7 // D8
|
|
||||||
SPI1_MISO_PIN = GPIO8 // D9
|
|
||||||
SPI1_MOSI_PIN = GPIO9 // D10
|
|
||||||
SPI1_CS_PIN = NoPin
|
|
||||||
|
|
||||||
SPI2_SCK_PIN = NoPin
|
|
||||||
SPI2_MOSI_PIN = NoPin
|
|
||||||
SPI2_MISO_PIN = NoPin
|
|
||||||
SPI2_CS_PIN = NoPin
|
|
||||||
)
|
|
||||||
|
|
||||||
// Onboard LEDs
|
|
||||||
const (
|
|
||||||
LED = GPIO21
|
|
||||||
)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
|
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
@@ -43,11 +43,6 @@ type BlockDevice interface {
|
|||||||
io.ReaderAt
|
io.ReaderAt
|
||||||
|
|
||||||
// WriteAt writes the given number of bytes to the block device.
|
// WriteAt writes the given number of bytes to the block device.
|
||||||
//
|
|
||||||
// This interface directly writes data to the underlying block device.
|
|
||||||
// Different kinds of devices have different requirements: most can only
|
|
||||||
// write data after the page has been erased, and many can only write data
|
|
||||||
// with specific alignment (such as 4-byte alignment).
|
|
||||||
io.WriterAt
|
io.WriterAt
|
||||||
|
|
||||||
// Size returns the number of bytes in this block device.
|
// Size returns the number of bytes in this block device.
|
||||||
|
|||||||
+1
-31
@@ -1,9 +1,6 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import "errors"
|
||||||
"errors"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrTimeoutRNG = errors.New("machine: RNG Timeout")
|
ErrTimeoutRNG = errors.New("machine: RNG Timeout")
|
||||||
@@ -65,30 +62,3 @@ func (p Pin) Low() {
|
|||||||
type ADC struct {
|
type ADC struct {
|
||||||
Pin Pin
|
Pin Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the pointer to a uintptr, to be used for memory I/O (DMA for
|
|
||||||
// example). It also means the pointer is "gone" as far as the compiler is
|
|
||||||
// concerned, and a GC cycle might deallocate the object. To prevent this from
|
|
||||||
// happening, also call keepAliveNoEscape at a point after the address isn't
|
|
||||||
// accessed anymore by the hardware.
|
|
||||||
// The only exception is if the pointer is accessed later in a volatile way
|
|
||||||
// (volatile read/write), which also forces the value to stay alive until that
|
|
||||||
// point.
|
|
||||||
//
|
|
||||||
// This function is treated specially by the compiler to mark the 'ptr'
|
|
||||||
// parameter as not escaping.
|
|
||||||
//
|
|
||||||
// TODO: this function should eventually be replaced with the proposed ptrtoaddr
|
|
||||||
// instruction in LLVM. See:
|
|
||||||
// https://discourse.llvm.org/t/clarifiying-the-semantics-of-ptrtoint/83987/10
|
|
||||||
// https://github.com/llvm/llvm-project/pull/139357
|
|
||||||
func unsafeNoEscape(ptr unsafe.Pointer) uintptr {
|
|
||||||
return uintptr(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the given pointer stays alive until this point. This is similar to
|
|
||||||
// runtime.KeepAlive, with the difference that it won't let the pointer escape.
|
|
||||||
// This is typically used together with unsafeNoEscape.
|
|
||||||
//
|
|
||||||
// This is a compiler intrinsic.
|
|
||||||
func keepAliveNoEscape(ptr unsafe.Pointer)
|
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
|||||||
|
|
||||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||||
sendUSBPacket(ep, data)
|
sendUSBPacket(ep, data, 0)
|
||||||
|
|
||||||
// clear transfer complete flag
|
// clear transfer complete flag
|
||||||
setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||||
@@ -351,28 +351,27 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
|
|||||||
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
||||||
//
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func sendUSBPacket(ep uint32, data []byte) {
|
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||||
// Select the corresponding buffer.
|
l := uint16(len(data))
|
||||||
buffer := udd_ep_control_cache_buffer[:]
|
if 0 < maxsize && maxsize < l {
|
||||||
if ep != 0 {
|
l = maxsize
|
||||||
buffer = udd_ep_in_cache_buffer[ep][:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the packet to the buffer.
|
// Set endpoint address for sending data
|
||||||
copy(buffer[:len(data)], data)
|
if ep == 0 {
|
||||||
|
copy(udd_ep_control_cache_buffer[:], data[:l])
|
||||||
// Select the corresponding endpoint descriptor.
|
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
|
||||||
endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1]
|
} else {
|
||||||
|
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||||
// Set the endpoint address.
|
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||||
endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer)))))
|
}
|
||||||
|
|
||||||
// clear multi-packet size which is total bytes already sent
|
// clear multi-packet size which is total bytes already sent
|
||||||
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||||
|
|
||||||
// set byte count, which is total number of bytes to be sent
|
// set byte count, which is total number of bytes to be sent
|
||||||
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
endpoint.PCKSIZE.SetBits((uint32(len(data)) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
|||||||
|
|
||||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||||
sendUSBPacket(ep, data)
|
sendUSBPacket(ep, data, 0)
|
||||||
|
|
||||||
// clear transfer complete flag
|
// clear transfer complete flag
|
||||||
setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||||
@@ -354,28 +354,27 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
|
|||||||
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
||||||
//
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func sendUSBPacket(ep uint32, data []byte) {
|
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||||
// Select the corresponding buffer.
|
l := uint16(len(data))
|
||||||
buffer := udd_ep_control_cache_buffer[:]
|
if 0 < maxsize && maxsize < l {
|
||||||
if ep != 0 {
|
l = maxsize
|
||||||
buffer = udd_ep_in_cache_buffer[ep][:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the packet to the buffer.
|
// Set endpoint address for sending data
|
||||||
copy(buffer[:len(data)], data)
|
if ep == 0 {
|
||||||
|
copy(udd_ep_control_cache_buffer[:], data[:l])
|
||||||
// Select the corresponding endpoint descriptor.
|
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
|
||||||
endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1]
|
} else {
|
||||||
|
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||||
// Set the endpoint address.
|
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||||
endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer)))))
|
}
|
||||||
|
|
||||||
// clear multi-packet size which is total bytes already sent
|
// clear multi-packet size which is total bytes already sent
|
||||||
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||||
|
|
||||||
// set byte count, which is total number of bytes to be sent
|
// set byte count, which is total number of bytes to be sent
|
||||||
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
endpoint.PCKSIZE.SetBits((uint32(len(data)) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||||
|
|||||||
@@ -21,524 +21,3 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
|||||||
// Very simple for the attiny85, which only has a single port.
|
// Very simple for the attiny85, which only has a single port.
|
||||||
return avr.PORTB, 1 << uint8(p)
|
return avr.PORTB, 1 << uint8(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PWM is one PWM peripheral, which consists of a counter and two output
|
|
||||||
// channels (that can be connected to two fixed pins). You can set the frequency
|
|
||||||
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
|
||||||
// once.
|
|
||||||
type PWM struct {
|
|
||||||
num uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
Timer0 = PWM{0} // 8 bit timer for PB0 and PB1
|
|
||||||
Timer1 = PWM{1} // 8 bit high-speed timer for PB1 and PB4
|
|
||||||
)
|
|
||||||
|
|
||||||
// GTCCR bits for Timer1 that are not defined in the device file
|
|
||||||
const (
|
|
||||||
gtccrPWM1B = 0x40 // Pulse Width Modulator B Enable
|
|
||||||
gtccrCOM1B0 = 0x10 // Comparator B Output Mode bit 0
|
|
||||||
gtccrCOM1B1 = 0x20 // Comparator B Output Mode bit 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// Configure enables and configures this PWM.
|
|
||||||
//
|
|
||||||
// For Timer0, there is only a limited number of periods available, namely the
|
|
||||||
// CPU frequency divided by 256 and again divided by 1, 8, 64, 256, or 1024.
|
|
||||||
// For a MCU running at 8MHz, this would be a period of 32µs, 256µs, 2048µs,
|
|
||||||
// 8192µs, or 32768µs.
|
|
||||||
//
|
|
||||||
// For Timer1, the period is more flexible as it uses OCR1C as the top value.
|
|
||||||
// Timer1 also supports more prescaler values (1 to 16384).
|
|
||||||
func (pwm PWM) Configure(config PWMConfig) error {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0: // Timer/Counter 0 (8-bit)
|
|
||||||
// Calculate the timer prescaler.
|
|
||||||
var prescaler uint8
|
|
||||||
switch config.Period {
|
|
||||||
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 1
|
|
||||||
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 2
|
|
||||||
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 3
|
|
||||||
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 4
|
|
||||||
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 5
|
|
||||||
default:
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
avr.TCCR0B.Set(prescaler)
|
|
||||||
// Set the PWM mode to fast PWM (mode = 3).
|
|
||||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
|
||||||
|
|
||||||
case 1: // Timer/Counter 1 (8-bit high-speed)
|
|
||||||
// Timer1 on ATtiny85 is different from ATmega328:
|
|
||||||
// - It's 8-bit with configurable top (OCR1C)
|
|
||||||
// - Has more prescaler options (1-16384)
|
|
||||||
// - PWM mode is enabled per-channel via PWM1A/PWM1B bits
|
|
||||||
var top uint64
|
|
||||||
if config.Period == 0 {
|
|
||||||
// Use a top appropriate for LEDs.
|
|
||||||
top = 0xff
|
|
||||||
} else {
|
|
||||||
// Calculate top value: top = period * (CPUFrequency / 1e9)
|
|
||||||
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timer1 prescaler values: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384
|
|
||||||
const maxTop = 256
|
|
||||||
var prescaler uint8
|
|
||||||
switch {
|
|
||||||
case top <= maxTop:
|
|
||||||
prescaler = 1 // prescaler 1
|
|
||||||
case top/2 <= maxTop:
|
|
||||||
prescaler = 2 // prescaler 2
|
|
||||||
top /= 2
|
|
||||||
case top/4 <= maxTop:
|
|
||||||
prescaler = 3 // prescaler 4
|
|
||||||
top /= 4
|
|
||||||
case top/8 <= maxTop:
|
|
||||||
prescaler = 4 // prescaler 8
|
|
||||||
top /= 8
|
|
||||||
case top/16 <= maxTop:
|
|
||||||
prescaler = 5 // prescaler 16
|
|
||||||
top /= 16
|
|
||||||
case top/32 <= maxTop:
|
|
||||||
prescaler = 6 // prescaler 32
|
|
||||||
top /= 32
|
|
||||||
case top/64 <= maxTop:
|
|
||||||
prescaler = 7 // prescaler 64
|
|
||||||
top /= 64
|
|
||||||
case top/128 <= maxTop:
|
|
||||||
prescaler = 8 // prescaler 128
|
|
||||||
top /= 128
|
|
||||||
case top/256 <= maxTop:
|
|
||||||
prescaler = 9 // prescaler 256
|
|
||||||
top /= 256
|
|
||||||
case top/512 <= maxTop:
|
|
||||||
prescaler = 10 // prescaler 512
|
|
||||||
top /= 512
|
|
||||||
case top/1024 <= maxTop:
|
|
||||||
prescaler = 11 // prescaler 1024
|
|
||||||
top /= 1024
|
|
||||||
case top/2048 <= maxTop:
|
|
||||||
prescaler = 12 // prescaler 2048
|
|
||||||
top /= 2048
|
|
||||||
case top/4096 <= maxTop:
|
|
||||||
prescaler = 13 // prescaler 4096
|
|
||||||
top /= 4096
|
|
||||||
case top/8192 <= maxTop:
|
|
||||||
prescaler = 14 // prescaler 8192
|
|
||||||
top /= 8192
|
|
||||||
case top/16384 <= maxTop:
|
|
||||||
prescaler = 15 // prescaler 16384
|
|
||||||
top /= 16384
|
|
||||||
default:
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set prescaler (CS1[3:0] bits)
|
|
||||||
avr.TCCR1.Set(prescaler)
|
|
||||||
// Set top value
|
|
||||||
avr.OCR1C.Set(uint8(top - 1))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPeriod updates the period of this PWM peripheral.
|
|
||||||
// To set a particular frequency, use the following formula:
|
|
||||||
//
|
|
||||||
// period = 1e9 / frequency
|
|
||||||
//
|
|
||||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
|
||||||
//
|
|
||||||
// SetPeriod will not change the prescaler, but also won't change the current
|
|
||||||
// value in any of the channels. This means that you may need to update the
|
|
||||||
// value for the particular channel.
|
|
||||||
//
|
|
||||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
|
||||||
// been configured. If you want to switch between frequencies, pick the lowest
|
|
||||||
// frequency (longest period) once when calling Configure and adjust the
|
|
||||||
// frequency here as needed.
|
|
||||||
func (pwm PWM) SetPeriod(period uint64) error {
|
|
||||||
if pwm.num == 0 {
|
|
||||||
return ErrPWMPeriodTooLong // Timer0 doesn't support dynamic period
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timer1 can adjust period via OCR1C
|
|
||||||
var top uint64
|
|
||||||
if period == 0 {
|
|
||||||
top = 0xff
|
|
||||||
} else {
|
|
||||||
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current prescaler
|
|
||||||
prescaler := avr.TCCR1.Get() & 0x0f
|
|
||||||
// Timer1 prescaler values follow a power-of-2 pattern:
|
|
||||||
// prescaler n maps to divisor 2^(n-1), so we can use a simple shift
|
|
||||||
if prescaler > 0 && prescaler <= 15 {
|
|
||||||
top >>= (prescaler - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if top > 256 {
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
avr.OCR1C.Set(uint8(top - 1))
|
|
||||||
avr.TCNT1.Set(0)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
|
||||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
|
||||||
// constant.
|
|
||||||
//
|
|
||||||
// The value returned here is hardware dependent. In general, it's best to treat
|
|
||||||
// it as an opaque value that can be divided by some number and passed to Set
|
|
||||||
// (see Set documentation for more information).
|
|
||||||
func (pwm PWM) Top() uint32 {
|
|
||||||
if pwm.num == 1 {
|
|
||||||
// Timer1 has configurable top via OCR1C
|
|
||||||
return uint32(avr.OCR1C.Get()) + 1
|
|
||||||
}
|
|
||||||
// Timer0 goes from 0 to 0xff (256 in total)
|
|
||||||
return 256
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter returns the current counter value of the timer in this PWM
|
|
||||||
// peripheral. It may be useful for debugging.
|
|
||||||
func (pwm PWM) Counter() uint32 {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
return uint32(avr.TCNT0.Get())
|
|
||||||
case 1:
|
|
||||||
return uint32(avr.TCNT1.Get())
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prescaler lookup tables using uint16 (more efficient than uint64 on AVR)
|
|
||||||
// Timer0 prescaler lookup table (index 0-7 maps to prescaler bits)
|
|
||||||
var timer0Prescalers = [8]uint16{0, 1, 8, 64, 256, 1024, 0, 0}
|
|
||||||
|
|
||||||
// Timer1 prescaler lookup table (index 0-15 maps to prescaler bits)
|
|
||||||
var timer1Prescalers = [16]uint16{0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384}
|
|
||||||
|
|
||||||
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
|
||||||
// from the configured period due to rounding.
|
|
||||||
func (pwm PWM) Period() uint64 {
|
|
||||||
var prescaler uint64
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
prescalerBits := avr.TCCR0B.Get() & 0x7
|
|
||||||
prescaler = uint64(timer0Prescalers[prescalerBits])
|
|
||||||
if prescaler == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
prescalerBits := avr.TCCR1.Get() & 0x0f
|
|
||||||
prescaler = uint64(timer1Prescalers[prescalerBits])
|
|
||||||
if prescaler == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
top := uint64(pwm.Top())
|
|
||||||
return prescaler * top * 1000 / uint64(CPUFrequency()/1e6)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Channel returns a PWM channel for the given pin.
|
|
||||||
func (pwm PWM) Channel(pin Pin) (uint8, error) {
|
|
||||||
pin.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
pin.Low()
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
switch pin {
|
|
||||||
case PB0: // OC0A
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
|
||||||
return 0, nil
|
|
||||||
case PB1: // OC0B
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
switch pin {
|
|
||||||
case PB1: // OC1A
|
|
||||||
// Enable PWM on channel A
|
|
||||||
avr.TCCR1.SetBits(avr.TCCR1_PWM1A | avr.TCCR1_COM1A1)
|
|
||||||
return 0, nil
|
|
||||||
case PB4: // OC1B
|
|
||||||
// Enable PWM on channel B (controlled via GTCCR)
|
|
||||||
avr.GTCCR.SetBits(gtccrPWM1B | gtccrCOM1B1)
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, ErrInvalidOutputPin
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetInverting sets whether to invert the output of this channel.
|
|
||||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
|
||||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
|
||||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
|
||||||
// high with a duty cycle of 25%.
|
|
||||||
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A, PB0
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 0)
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 0)
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
|
||||||
}
|
|
||||||
case 1: // channel B, PB1
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 1)
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 1)
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A, PB1
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 1)
|
|
||||||
avr.TCCR1.SetBits(avr.TCCR1_COM1A0)
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 1)
|
|
||||||
avr.TCCR1.ClearBits(avr.TCCR1_COM1A0)
|
|
||||||
}
|
|
||||||
case 1: // channel B, PB4
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 4)
|
|
||||||
avr.GTCCR.SetBits(gtccrCOM1B0)
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 4)
|
|
||||||
avr.GTCCR.ClearBits(gtccrCOM1B0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updates the channel value. This is used to control the channel duty
|
|
||||||
// cycle, in other words the fraction of time the channel output is high (or low
|
|
||||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
|
||||||
//
|
|
||||||
// pwm.Set(channel, pwm.Top() / 4)
|
|
||||||
//
|
|
||||||
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
|
||||||
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
|
||||||
func (pwm PWM) Set(channel uint8, value uint32) {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A, PB0
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
|
|
||||||
} else {
|
|
||||||
avr.OCR0A.Set(uint8(value - 1))
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
|
||||||
}
|
|
||||||
case 1: // channel B, PB1
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
|
|
||||||
} else {
|
|
||||||
avr.OCR0B.Set(uint8(value - 1))
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A, PB1
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR1.ClearBits(avr.TCCR1_COM1A1)
|
|
||||||
} else {
|
|
||||||
avr.OCR1A.Set(uint8(value - 1))
|
|
||||||
avr.TCCR1.SetBits(avr.TCCR1_COM1A1)
|
|
||||||
}
|
|
||||||
case 1: // channel B, PB4
|
|
||||||
if value == 0 {
|
|
||||||
avr.GTCCR.ClearBits(gtccrCOM1B1)
|
|
||||||
} else {
|
|
||||||
avr.OCR1B.Set(uint8(value - 1))
|
|
||||||
avr.GTCCR.SetBits(gtccrCOM1B1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPIConfig is used to store config info for SPI.
|
|
||||||
type SPIConfig struct {
|
|
||||||
Frequency uint32
|
|
||||||
LSBFirst bool
|
|
||||||
Mode uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI is the USI-based SPI implementation for ATTiny85.
|
|
||||||
// The ATTiny85 doesn't have dedicated SPI hardware, but uses the USI
|
|
||||||
// (Universal Serial Interface) in three-wire mode.
|
|
||||||
//
|
|
||||||
// Fixed pin mapping (directly controlled by USI hardware):
|
|
||||||
// - PB2: SCK (clock)
|
|
||||||
// - PB1: DO/MOSI (data out)
|
|
||||||
// - PB0: DI/MISO (data in)
|
|
||||||
//
|
|
||||||
// Note: CS pin must be managed by the user.
|
|
||||||
type SPI struct {
|
|
||||||
// Delay cycles for frequency control (0 = max speed)
|
|
||||||
delayCycles uint16
|
|
||||||
|
|
||||||
// USICR value configured for the selected SPI mode
|
|
||||||
usicrValue uint8
|
|
||||||
|
|
||||||
// LSB-first mode (requires software bit reversal)
|
|
||||||
lsbFirst bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI0 is the USI-based SPI interface on the ATTiny85
|
|
||||||
var SPI0 = SPI{}
|
|
||||||
|
|
||||||
// Configure sets up the USI for SPI communication.
|
|
||||||
// Note: The user must configure and control the CS pin separately.
|
|
||||||
func (s *SPI) Configure(config SPIConfig) error {
|
|
||||||
// Configure USI pins (fixed by hardware)
|
|
||||||
// PB1 (DO/MOSI) -> OUTPUT
|
|
||||||
// PB2 (USCK/SCK) -> OUTPUT
|
|
||||||
// PB0 (DI/MISO) -> INPUT
|
|
||||||
PB1.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
PB2.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
PB0.Configure(PinConfig{Mode: PinInput})
|
|
||||||
|
|
||||||
// Reset USI registers
|
|
||||||
avr.USIDR.Set(0)
|
|
||||||
avr.USISR.Set(0)
|
|
||||||
|
|
||||||
// Configure USI for SPI mode:
|
|
||||||
// - USIWM0: Three-wire mode (SPI)
|
|
||||||
// - USICS1: External clock source (software controlled via USITC)
|
|
||||||
// - USICLK: Clock strobe - enables counter increment on USITC toggle
|
|
||||||
// - USICS0: Controls clock phase (CPHA)
|
|
||||||
//
|
|
||||||
// SPI Modes:
|
|
||||||
// Mode 0 (CPOL=0, CPHA=0): Clock idle low, sample on rising edge
|
|
||||||
// Mode 1 (CPOL=0, CPHA=1): Clock idle low, sample on falling edge
|
|
||||||
// Mode 2 (CPOL=1, CPHA=0): Clock idle high, sample on falling edge
|
|
||||||
// Mode 3 (CPOL=1, CPHA=1): Clock idle high, sample on rising edge
|
|
||||||
//
|
|
||||||
// For USI, USICS0 controls the sampling edge when USICS1=1:
|
|
||||||
// USICS0=0: Positive edge (rising)
|
|
||||||
// USICS0=1: Negative edge (falling)
|
|
||||||
switch config.Mode {
|
|
||||||
case Mode0: // CPOL=0, CPHA=0: idle low, sample rising
|
|
||||||
PB2.Low()
|
|
||||||
s.usicrValue = avr.USICR_USIWM0 | avr.USICR_USICS1 | avr.USICR_USICLK
|
|
||||||
case Mode1: // CPOL=0, CPHA=1: idle low, sample falling
|
|
||||||
PB2.Low()
|
|
||||||
s.usicrValue = avr.USICR_USIWM0 | avr.USICR_USICS1 | avr.USICR_USICS0 | avr.USICR_USICLK
|
|
||||||
case Mode2: // CPOL=1, CPHA=0: idle high, sample falling
|
|
||||||
PB2.High()
|
|
||||||
s.usicrValue = avr.USICR_USIWM0 | avr.USICR_USICS1 | avr.USICR_USICS0 | avr.USICR_USICLK
|
|
||||||
case Mode3: // CPOL=1, CPHA=1: idle high, sample rising
|
|
||||||
PB2.High()
|
|
||||||
s.usicrValue = avr.USICR_USIWM0 | avr.USICR_USICS1 | avr.USICR_USICLK
|
|
||||||
default: // Default to Mode 0
|
|
||||||
PB2.Low()
|
|
||||||
s.usicrValue = avr.USICR_USIWM0 | avr.USICR_USICS1 | avr.USICR_USICLK
|
|
||||||
}
|
|
||||||
avr.USICR.Set(s.usicrValue)
|
|
||||||
|
|
||||||
// Calculate delay cycles for frequency control
|
|
||||||
// Each bit transfer requires 2 clock toggles (rising + falling edge)
|
|
||||||
// The loop overhead is approximately 10-15 cycles per toggle on AVR
|
|
||||||
// We calculate additional delay cycles needed to achieve the target frequency
|
|
||||||
if config.Frequency > 0 && config.Frequency < CPUFrequency()/2 {
|
|
||||||
// Cycles per half-period = CPUFrequency / (2 * Frequency)
|
|
||||||
// Subtract loop overhead (~15 cycles) to get delay cycles
|
|
||||||
cyclesPerHalfPeriod := CPUFrequency() / (2 * config.Frequency)
|
|
||||||
const loopOverhead = 15
|
|
||||||
if cyclesPerHalfPeriod > loopOverhead {
|
|
||||||
s.delayCycles = uint16(cyclesPerHalfPeriod - loopOverhead)
|
|
||||||
} else {
|
|
||||||
s.delayCycles = 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Max speed - no delay
|
|
||||||
s.delayCycles = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store LSBFirst setting for use in Transfer
|
|
||||||
s.lsbFirst = config.LSBFirst
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// reverseByte reverses the bit order of a byte (MSB <-> LSB)
|
|
||||||
// Used for LSB-first SPI mode since USI hardware only supports MSB-first
|
|
||||||
func reverseByte(b byte) byte {
|
|
||||||
b = (b&0xF0)>>4 | (b&0x0F)<<4
|
|
||||||
b = (b&0xCC)>>2 | (b&0x33)<<2
|
|
||||||
b = (b&0xAA)>>1 | (b&0x55)<<1
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer performs a single byte SPI transfer (send and receive simultaneously)
|
|
||||||
// This implements the USI-based SPI transfer using the "clock strobing" technique
|
|
||||||
func (s *SPI) Transfer(b byte) (byte, error) {
|
|
||||||
// For LSB-first mode, reverse the bits before sending
|
|
||||||
// USI hardware only supports MSB-first, so we do it in software
|
|
||||||
if s.lsbFirst {
|
|
||||||
b = reverseByte(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the byte to transmit into the USI Data Register
|
|
||||||
avr.USIDR.Set(b)
|
|
||||||
|
|
||||||
// Clear the counter overflow flag by writing 1 to it (AVR quirk)
|
|
||||||
// This also resets the 4-bit counter to 0
|
|
||||||
avr.USISR.Set(avr.USISR_USIOIF)
|
|
||||||
|
|
||||||
// Clock the data out/in
|
|
||||||
// We need 16 clock toggles (8 bits × 2 edges per bit)
|
|
||||||
// The USI counter counts each clock edge, so it overflows at 16
|
|
||||||
// After 16 toggles, the clock returns to its idle state (set by CPOL in Configure)
|
|
||||||
//
|
|
||||||
// IMPORTANT: Only toggle USITC here!
|
|
||||||
// - USITC toggles the clock pin
|
|
||||||
// - The USICR mode bits (USIWM0, USICS1, USICS0, USICLK) were set in Configure()
|
|
||||||
// - SetBits preserves those bits and only sets USITC
|
|
||||||
if s.delayCycles == 0 {
|
|
||||||
// Fast path: no delay, run at maximum speed
|
|
||||||
for !avr.USISR.HasBits(avr.USISR_USIOIF) {
|
|
||||||
avr.USICR.SetBits(avr.USICR_USITC)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Frequency-controlled path: add delay between clock toggles
|
|
||||||
for !avr.USISR.HasBits(avr.USISR_USIOIF) {
|
|
||||||
avr.USICR.SetBits(avr.USICR_USITC)
|
|
||||||
// Delay loop for frequency control
|
|
||||||
// Each iteration is approximately 3 cycles on AVR (dec, brne)
|
|
||||||
for i := s.delayCycles; i > 0; i-- {
|
|
||||||
avr.Asm("nop")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the received byte
|
|
||||||
result := avr.USIDR.Get()
|
|
||||||
|
|
||||||
// For LSB-first mode, reverse the received bits
|
|
||||||
if s.lsbFirst {
|
|
||||||
result = reverseByte(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -509,6 +509,98 @@ func (uart *UART) writeByte(b byte) error {
|
|||||||
|
|
||||||
func (uart *UART) flush() {}
|
func (uart *UART) flush() {}
|
||||||
|
|
||||||
|
type Serialer interface {
|
||||||
|
WriteByte(c byte) error
|
||||||
|
Write(data []byte) (n int, err error)
|
||||||
|
Configure(config UARTConfig) error
|
||||||
|
Buffered() int
|
||||||
|
ReadByte() (byte, error)
|
||||||
|
DTR() bool
|
||||||
|
RTS() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// USB Serial/JTAG Controller
|
||||||
|
// See esp32-c3_technical_reference_manual_en.pdf
|
||||||
|
// pg. 736
|
||||||
|
type USB_DEVICE struct {
|
||||||
|
Bus *esp.USB_DEVICE_Type
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_USBCDC = &USB_DEVICE{
|
||||||
|
Bus: esp.USB_DEVICE,
|
||||||
|
}
|
||||||
|
|
||||||
|
USBCDC Serialer = _USBCDC
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errUSBWrongSize = errors.New("USB: invalid write size")
|
||||||
|
errUSBCouldNotWriteAllData = errors.New("USB: could not write all data")
|
||||||
|
errUSBBufferEmpty = errors.New("USB: read buffer empty")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) Configure(config UARTConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) WriteByte(c byte) error {
|
||||||
|
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||||
|
return errUSBCouldNotWriteAllData
|
||||||
|
}
|
||||||
|
|
||||||
|
usbdev.Bus.SetEP1_RDWR_BYTE(uint32(c))
|
||||||
|
usbdev.flush()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) Write(data []byte) (n int, err error) {
|
||||||
|
if len(data) == 0 || len(data) > 64 {
|
||||||
|
return 0, errUSBWrongSize
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range data {
|
||||||
|
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||||
|
if i > 0 {
|
||||||
|
usbdev.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
return i, errUSBCouldNotWriteAllData
|
||||||
|
}
|
||||||
|
usbdev.Bus.SetEP1_RDWR_BYTE(uint32(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
usbdev.flush()
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) Buffered() int {
|
||||||
|
return int(usbdev.Bus.GetEP1_CONF_SERIAL_OUT_EP_DATA_AVAIL())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) ReadByte() (byte, error) {
|
||||||
|
if usbdev.Bus.GetEP1_CONF_SERIAL_OUT_EP_DATA_AVAIL() != 0 {
|
||||||
|
return byte(usbdev.Bus.GetEP1_RDWR_BYTE()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) DTR() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) RTS() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbdev *USB_DEVICE) flush() {
|
||||||
|
usbdev.Bus.SetEP1_CONF_WR_DONE(1)
|
||||||
|
for usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetRNG returns 32-bit random numbers using the ESP32-C3 true random number generator,
|
// GetRNG returns 32-bit random numbers using the ESP32-C3 true random number generator,
|
||||||
// Random numbers are generated based on the thermal noise in the system and the
|
// Random numbers are generated based on the thermal noise in the system and the
|
||||||
// asynchronous clock mismatch.
|
// asynchronous clock mismatch.
|
||||||
|
|||||||
@@ -1,310 +0,0 @@
|
|||||||
//go:build esp32s3
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/esp"
|
|
||||||
"errors"
|
|
||||||
"runtime/volatile"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const deviceName = esp.Device
|
|
||||||
|
|
||||||
const xtalClock = 40_000000 // 40MHz
|
|
||||||
const apbClock = 80_000000 // 80MHz
|
|
||||||
const cryptoPWMClock = 160_000000 // 160MHz
|
|
||||||
|
|
||||||
// GetCPUFrequency returns the current CPU frequency of the chip.
|
|
||||||
func GetCPUFrequency() (uint32, error) {
|
|
||||||
switch esp.SYSTEM.GetSYSCLK_CONF_SOC_CLK_SEL() {
|
|
||||||
case 0:
|
|
||||||
return xtalClock / (esp.SYSTEM.GetSYSCLK_CONF_PRE_DIV_CNT() + 1), nil
|
|
||||||
case 1:
|
|
||||||
switch esp.SYSTEM.GetCPU_PER_CONF_CPUPERIOD_SEL() {
|
|
||||||
case 0:
|
|
||||||
return 80e6, nil
|
|
||||||
case 1:
|
|
||||||
return 160e6, nil
|
|
||||||
case 2:
|
|
||||||
// If esp.SYSTEM.GetCPU_PER_CONF_PLL_FREQ_SEL() == 1, this is undefined
|
|
||||||
return 240e6, nil
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
//RC Fast Clock
|
|
||||||
return (175e5) / (esp.SYSTEM.GetSYSCLK_CONF_PRE_DIV_CNT() + 1), nil
|
|
||||||
}
|
|
||||||
return 0, errors.New("machine: Unable to determine current cpu frequency")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCPUFrequency sets the frequency of the CPU to one of several targets
|
|
||||||
func SetCPUFrequency(frequency uint32) error {
|
|
||||||
// Always assume we are on PLL. Lower frequencies can be set with a different
|
|
||||||
// clock source, but this will change the behavior of APB clock and Crypto PWM
|
|
||||||
// clock
|
|
||||||
//esp.SYSTEM.SetSYSCLK_CONF_SOC_CLK_SEL(1)
|
|
||||||
|
|
||||||
switch frequency {
|
|
||||||
case 80_000000:
|
|
||||||
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(0)
|
|
||||||
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(0) // Reduce PLL freq when possible
|
|
||||||
return nil
|
|
||||||
case 160_000000:
|
|
||||||
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(1)
|
|
||||||
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(0)
|
|
||||||
return nil
|
|
||||||
case 240_000000:
|
|
||||||
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(1) // Increase PLL freq when needed
|
|
||||||
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(2)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("machine: Unsupported CPU frequency selected. Supported: 80, 160, 240 MHz")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
PinOutput PinMode = iota
|
|
||||||
PinInput
|
|
||||||
PinInputPullup
|
|
||||||
PinInputPulldown
|
|
||||||
)
|
|
||||||
|
|
||||||
// Hardware pin numbers
|
|
||||||
const (
|
|
||||||
GPIO0 Pin = 0
|
|
||||||
GPIO1 Pin = 1
|
|
||||||
GPIO2 Pin = 2
|
|
||||||
GPIO3 Pin = 3
|
|
||||||
GPIO4 Pin = 4
|
|
||||||
GPIO5 Pin = 5
|
|
||||||
GPIO6 Pin = 6
|
|
||||||
GPIO7 Pin = 7
|
|
||||||
GPIO8 Pin = 8
|
|
||||||
GPIO9 Pin = 9
|
|
||||||
GPIO10 Pin = 10
|
|
||||||
GPIO11 Pin = 11
|
|
||||||
GPIO12 Pin = 12
|
|
||||||
GPIO13 Pin = 13
|
|
||||||
GPIO14 Pin = 14
|
|
||||||
GPIO15 Pin = 15
|
|
||||||
GPIO16 Pin = 16
|
|
||||||
GPIO17 Pin = 17
|
|
||||||
GPIO18 Pin = 18
|
|
||||||
GPIO19 Pin = 19
|
|
||||||
GPIO20 Pin = 20
|
|
||||||
GPIO21 Pin = 21
|
|
||||||
GPIO26 Pin = 26
|
|
||||||
GPIO27 Pin = 27
|
|
||||||
GPIO28 Pin = 28
|
|
||||||
GPIO29 Pin = 29
|
|
||||||
GPIO30 Pin = 30
|
|
||||||
GPIO31 Pin = 31
|
|
||||||
GPIO32 Pin = 32
|
|
||||||
GPIO33 Pin = 33
|
|
||||||
GPIO34 Pin = 34
|
|
||||||
GPIO35 Pin = 35
|
|
||||||
GPIO36 Pin = 36
|
|
||||||
GPIO37 Pin = 37
|
|
||||||
GPIO38 Pin = 38
|
|
||||||
GPIO39 Pin = 39
|
|
||||||
GPIO40 Pin = 40
|
|
||||||
GPIO41 Pin = 41
|
|
||||||
GPIO42 Pin = 42
|
|
||||||
GPIO43 Pin = 43
|
|
||||||
GPIO44 Pin = 44
|
|
||||||
GPIO45 Pin = 45
|
|
||||||
GPIO46 Pin = 46
|
|
||||||
GPIO47 Pin = 47
|
|
||||||
GPIO48 Pin = 48
|
|
||||||
)
|
|
||||||
|
|
||||||
// Configure this pin with the given configuration.
|
|
||||||
func (p Pin) Configure(config PinConfig) {
|
|
||||||
// Output function 256 is a special value reserved for use as a regular GPIO
|
|
||||||
// pin. Peripherals (SPI etc) can set a custom output function by calling
|
|
||||||
// lowercase configure() instead with a signal name.
|
|
||||||
p.configure(config, 256)
|
|
||||||
}
|
|
||||||
|
|
||||||
// configure is the same as Configure, but allows for setting a specific input
|
|
||||||
// or output signal.
|
|
||||||
// Signals are always routed through the GPIO matrix for simplicity. Output
|
|
||||||
// signals are configured in FUNCx_OUT_SEL_CFG which selects a particular signal
|
|
||||||
// to output on a given pin. Input signals are configured in FUNCy_IN_SEL_CFG,
|
|
||||||
// which sets the pin to use for a particular input signal.
|
|
||||||
func (p Pin) configure(config PinConfig, signal uint32) {
|
|
||||||
if p == NoPin {
|
|
||||||
// This simplifies pin configuration in peripherals such as SPI.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ioConfig := uint32(0)
|
|
||||||
|
|
||||||
// MCU_SEL: Function 1 is always GPIO
|
|
||||||
ioConfig |= (1 << esp.IO_MUX_GPIO_MCU_SEL_Pos)
|
|
||||||
|
|
||||||
// FUN_IE: Make this pin an input pin (always set for GPIO operation)
|
|
||||||
ioConfig |= esp.IO_MUX_GPIO_FUN_IE
|
|
||||||
|
|
||||||
// DRV: Set drive strength to 20 mA as a default. Pins 17 and 18 are special
|
|
||||||
var drive uint32
|
|
||||||
if p == GPIO17 || p == GPIO18 {
|
|
||||||
drive = 1 // 20 mA
|
|
||||||
} else {
|
|
||||||
drive = 2 // 20 mA
|
|
||||||
}
|
|
||||||
ioConfig |= (drive << esp.IO_MUX_GPIO_FUN_DRV_Pos)
|
|
||||||
|
|
||||||
// WPU/WPD: Select pull mode.
|
|
||||||
if config.Mode == PinInputPullup {
|
|
||||||
ioConfig |= esp.IO_MUX_GPIO_FUN_WPU
|
|
||||||
} else if config.Mode == PinInputPulldown {
|
|
||||||
ioConfig |= esp.IO_MUX_GPIO_FUN_WPD
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set configuration
|
|
||||||
ioRegister := p.ioMuxReg()
|
|
||||||
ioRegister.Set(ioConfig)
|
|
||||||
|
|
||||||
switch config.Mode {
|
|
||||||
case PinOutput:
|
|
||||||
// Set the 'output enable' bit.
|
|
||||||
if p < 32 {
|
|
||||||
esp.GPIO.ENABLE_W1TS.Set(1 << p)
|
|
||||||
} else {
|
|
||||||
esp.GPIO.ENABLE1_W1TS.Set(1 << (p - 32))
|
|
||||||
}
|
|
||||||
// Set the signal to read the output value from. It can be a peripheral
|
|
||||||
// output signal, or the special value 256 which indicates regular GPIO
|
|
||||||
// usage.
|
|
||||||
p.outFunc().Set(signal)
|
|
||||||
case PinInput, PinInputPullup, PinInputPulldown:
|
|
||||||
// Clear the 'output enable' bit.
|
|
||||||
if p < 32 {
|
|
||||||
esp.GPIO.ENABLE_W1TC.Set(1 << p)
|
|
||||||
} else {
|
|
||||||
esp.GPIO.ENABLE1_W1TC.Set(1 << (p - 32))
|
|
||||||
}
|
|
||||||
if signal != 256 {
|
|
||||||
// Signal is a peripheral function (not a simple GPIO). Connect this
|
|
||||||
// signal to the pin.
|
|
||||||
// Note that outFunc and inFunc work in the opposite direction.
|
|
||||||
// outFunc configures a pin to use a given output signal, while
|
|
||||||
// inFunc specifies a pin to use to read the signal from.
|
|
||||||
inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ioMuxReg returns the IO_MUX_n_REG register used for configuring the io mux for
|
|
||||||
// this pin
|
|
||||||
func (p Pin) ioMuxReg() *volatile.Register32 {
|
|
||||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.IO_MUX.GPIO0), uintptr(p)*4))
|
|
||||||
}
|
|
||||||
|
|
||||||
// outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the
|
|
||||||
// output function selection.
|
|
||||||
func (p Pin) outFunc() *volatile.Register32 {
|
|
||||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG), uintptr(p)*4))
|
|
||||||
}
|
|
||||||
|
|
||||||
// inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input
|
|
||||||
// function selection.
|
|
||||||
func inFunc(signal uint32) *volatile.Register32 {
|
|
||||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_IN_SEL_CFG), uintptr(signal)*4))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the pin to high or low.
|
|
||||||
// Warning: only use this on an output pin!
|
|
||||||
func (p Pin) Set(value bool) {
|
|
||||||
if value {
|
|
||||||
reg, mask := p.portMaskSet()
|
|
||||||
reg.Set(mask)
|
|
||||||
} else {
|
|
||||||
reg, mask := p.portMaskClear()
|
|
||||||
reg.Set(mask)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
|
||||||
// implement bit-banged drivers.
|
|
||||||
//
|
|
||||||
// Warning: only use this on an output pin!
|
|
||||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
|
||||||
reg, mask := p.portMaskSet()
|
|
||||||
return ®.Reg, mask
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the register and mask to disable a given GPIO pin. This can be used to
|
|
||||||
// implement bit-banged drivers.
|
|
||||||
//
|
|
||||||
// Warning: only use this on an output pin!
|
|
||||||
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
|
||||||
reg, mask := p.portMaskClear()
|
|
||||||
return ®.Reg, mask
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pin) portMaskSet() (*volatile.Register32, uint32) {
|
|
||||||
if p < 32 {
|
|
||||||
return &esp.GPIO.OUT_W1TS, 1 << p
|
|
||||||
} else {
|
|
||||||
return &esp.GPIO.OUT1_W1TS, 1 << (p - 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
|
|
||||||
if p < 32 {
|
|
||||||
return &esp.GPIO.OUT_W1TC, 1 << p
|
|
||||||
} else {
|
|
||||||
return &esp.GPIO.OUT1_W1TC, 1 << (p - 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the current value of a GPIO pin when the pin is configured as an
|
|
||||||
// input or as an output.
|
|
||||||
func (p Pin) Get() bool {
|
|
||||||
if p < 32 {
|
|
||||||
return esp.GPIO.IN.Get()&(1<<p) != 0
|
|
||||||
} else {
|
|
||||||
return esp.GPIO.IN1.Get()&(1<<(p-32)) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var DefaultUART = UART0
|
|
||||||
|
|
||||||
var (
|
|
||||||
UART0 = &_UART0
|
|
||||||
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
|
||||||
UART1 = &_UART1
|
|
||||||
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
|
||||||
UART2 = &_UART2
|
|
||||||
_UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()}
|
|
||||||
)
|
|
||||||
|
|
||||||
type UART struct {
|
|
||||||
Bus *esp.UART_Type
|
|
||||||
Buffer *RingBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (uart *UART) Configure(config UARTConfig) {
|
|
||||||
if config.BaudRate == 0 {
|
|
||||||
config.BaudRate = 115200
|
|
||||||
}
|
|
||||||
// Crystal clock source is selected by default
|
|
||||||
uart.Bus.CLKDIV.Set(xtalClock / config.BaudRate)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (uart *UART) writeByte(b byte) error {
|
|
||||||
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
|
|
||||||
// Read UART_TXFIFO_CNT from the status register, which indicates how
|
|
||||||
// many bytes there are in the transmit buffer. Wait until there are
|
|
||||||
// less than 128 bytes in this buffer (the default buffer size).
|
|
||||||
}
|
|
||||||
uart.Bus.FIFO.Set(uint32(b))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (uart *UART) flush() {}
|
|
||||||
@@ -1,460 +0,0 @@
|
|||||||
//go:build esp32s3
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// ESP32-S3 SPI support based on ESP-IDF HAL
|
|
||||||
// Simple but correct implementation following spi_ll.h
|
|
||||||
// SPI0 = hardware SPI2 (FSPI), SPI1 = hardware SPI3 (HSPI)
|
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/spi_master.html
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/esp"
|
|
||||||
"errors"
|
|
||||||
"runtime/volatile"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
SPI_MODE0 = uint8(0)
|
|
||||||
SPI_MODE1 = uint8(1)
|
|
||||||
SPI_MODE2 = uint8(2)
|
|
||||||
SPI_MODE3 = uint8(3)
|
|
||||||
|
|
||||||
// ESP32-S3 PLL clock frequency (same as ESP32-C3)
|
|
||||||
pplClockFreq = 80e6
|
|
||||||
|
|
||||||
// Default SPI frequency - maximum safe speed
|
|
||||||
SPI_DEFAULT_FREQUENCY = 80e6 // 80MHz
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// IO MUX function number for SPI direct connection
|
|
||||||
SPI_IOMUX_FUNC = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// ESP32-S3 GPIO Matrix signal indices for SPI - CORRECTED from ESP-IDF gpio_sig_map.h
|
|
||||||
const (
|
|
||||||
// SPI2 (FSPI) signals - Hardware SPI2 - CORRECT VALUES from ESP-IDF
|
|
||||||
SPI2_CLK_OUT_IDX = uint32(101) // FSPICLK_OUT_IDX
|
|
||||||
SPI2_CLK_IN_IDX = uint32(101) // FSPICLK_IN_IDX
|
|
||||||
SPI2_Q_OUT_IDX = uint32(102) // FSPIQ_OUT_IDX (MISO)
|
|
||||||
SPI2_Q_IN_IDX = uint32(102) // FSPIQ_IN_IDX
|
|
||||||
SPI2_D_OUT_IDX = uint32(103) // FSPID_OUT_IDX (MOSI)
|
|
||||||
SPI2_D_IN_IDX = uint32(103) // FSPID_IN_IDX
|
|
||||||
SPI2_CS0_OUT_IDX = uint32(110) // FSPICS0_OUT_IDX
|
|
||||||
|
|
||||||
// SPI3 (HSPI) signals - Hardware SPI3 - CORRECTED from ESP-IDF gpio_sig_map.h
|
|
||||||
// Source: /esp-idf/components/soc/esp32s3/include/soc/gpio_sig_map.h
|
|
||||||
SPI3_CLK_OUT_IDX = uint32(66) // Line 136: SPI3_CLK_OUT_IDX
|
|
||||||
SPI3_CLK_IN_IDX = uint32(66) // Line 135: SPI3_CLK_IN_IDX
|
|
||||||
SPI3_Q_OUT_IDX = uint32(67) // Line 138: SPI3_Q_OUT_IDX (MISO)
|
|
||||||
SPI3_Q_IN_IDX = uint32(67) // Line 137: SPI3_Q_IN_IDX
|
|
||||||
SPI3_D_OUT_IDX = uint32(68) // Line 140: SPI3_D_OUT_IDX (MOSI)
|
|
||||||
SPI3_D_IN_IDX = uint32(68) // Line 139: SPI3_D_IN_IDX
|
|
||||||
SPI3_CS0_OUT_IDX = uint32(71) // Line 146: SPI3_CS0_OUT_IDX
|
|
||||||
)
|
|
||||||
|
|
||||||
type SPI struct {
|
|
||||||
Bus interface{}
|
|
||||||
busID uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
SPI0 = &SPI{Bus: esp.SPI2, busID: 2} // Primary SPI (FSPI)
|
|
||||||
SPI1 = &SPI{Bus: esp.SPI3, busID: 3} // Secondary SPI (HSPI)
|
|
||||||
)
|
|
||||||
|
|
||||||
type SPIConfig struct {
|
|
||||||
Frequency uint32
|
|
||||||
SCK Pin // Serial Clock
|
|
||||||
SDO Pin // Serial Data Out (MOSI)
|
|
||||||
SDI Pin // Serial Data In (MISO)
|
|
||||||
CS Pin // Chip Select (optional)
|
|
||||||
LSBFirst bool // MSB is default
|
|
||||||
Mode uint8 // SPI_MODE0 is default
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure and make the SPI peripheral ready to use.
|
|
||||||
// Implementation following ESP-IDF HAL with GPIO Matrix routing
|
|
||||||
func (spi *SPI) Configure(config SPIConfig) error {
|
|
||||||
// Set default
|
|
||||||
if config.Frequency == 0 {
|
|
||||||
config.Frequency = SPI_DEFAULT_FREQUENCY
|
|
||||||
}
|
|
||||||
|
|
||||||
switch spi.busID {
|
|
||||||
case 2: // SPI2 (FSPI)
|
|
||||||
if config.SCK == 0 {
|
|
||||||
config.SCK = SPI1_SCK_PIN
|
|
||||||
}
|
|
||||||
if config.SDO == 0 {
|
|
||||||
config.SDO = SPI1_MOSI_PIN
|
|
||||||
}
|
|
||||||
if config.SDI == 0 {
|
|
||||||
config.SDI = SPI1_MISO_PIN
|
|
||||||
}
|
|
||||||
case 3: // SPI3 (HSPI)
|
|
||||||
if config.SCK == 0 {
|
|
||||||
config.SCK = SPI2_SCK_PIN
|
|
||||||
}
|
|
||||||
if config.SDO == 0 {
|
|
||||||
config.SDO = SPI2_MOSI_PIN
|
|
||||||
}
|
|
||||||
if config.SDI == 0 {
|
|
||||||
config.SDI = SPI2_MISO_PIN
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get GPIO Matrix signal indices for this SPI bus
|
|
||||||
var sckOutIdx, mosiOutIdx, misoInIdx, csOutIdx uint32
|
|
||||||
switch spi.busID {
|
|
||||||
case 2: // SPI2 (FSPI)
|
|
||||||
sckOutIdx = SPI2_CLK_OUT_IDX
|
|
||||||
mosiOutIdx = SPI2_D_OUT_IDX
|
|
||||||
misoInIdx = SPI2_Q_IN_IDX
|
|
||||||
csOutIdx = SPI2_CS0_OUT_IDX
|
|
||||||
case 3: // SPI3 (HSPI)
|
|
||||||
sckOutIdx = SPI3_CLK_OUT_IDX
|
|
||||||
mosiOutIdx = SPI3_D_OUT_IDX
|
|
||||||
misoInIdx = SPI3_Q_IN_IDX
|
|
||||||
csOutIdx = SPI3_CS0_OUT_IDX
|
|
||||||
default:
|
|
||||||
return ErrInvalidSPIBus
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we can use IO MUX direct connection for better performance
|
|
||||||
if isDefaultSPIPins(spi.busID, config) {
|
|
||||||
// Use IO MUX direct connection - better signal quality and performance
|
|
||||||
// Configure pins using IO MUX direct connection (SPI function)
|
|
||||||
if config.SCK != NoPin {
|
|
||||||
config.SCK.configure(PinConfig{Mode: PinOutput}, SPI_IOMUX_FUNC)
|
|
||||||
}
|
|
||||||
if config.SDO != NoPin {
|
|
||||||
config.SDO.configure(PinConfig{Mode: PinOutput}, SPI_IOMUX_FUNC)
|
|
||||||
}
|
|
||||||
if config.SDI != NoPin {
|
|
||||||
config.SDI.configure(PinConfig{Mode: PinInput}, SPI_IOMUX_FUNC)
|
|
||||||
}
|
|
||||||
if config.CS != NoPin {
|
|
||||||
config.CS.configure(PinConfig{Mode: PinOutput}, SPI_IOMUX_FUNC)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Use GPIO Matrix routing - more flexible but slightly slower
|
|
||||||
// Configure SDI (MISO) pin
|
|
||||||
if config.SDI != NoPin {
|
|
||||||
config.SDI.Configure(PinConfig{Mode: PinInput})
|
|
||||||
inFunc(misoInIdx).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.SDI))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure SDO (MOSI) pin
|
|
||||||
if config.SDO != NoPin {
|
|
||||||
config.SDO.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
config.SDO.outFunc().Set(mosiOutIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure SCK (Clock) pin
|
|
||||||
if config.SCK != NoPin {
|
|
||||||
config.SCK.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
config.SCK.outFunc().Set(sckOutIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure CS (Chip Select) pin
|
|
||||||
if config.CS != NoPin {
|
|
||||||
config.CS.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
config.CS.outFunc().Set(csOutIdx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable peripheral clock and reset
|
|
||||||
// Without bootloader, we need to be more explicit about clock initialization
|
|
||||||
switch spi.busID {
|
|
||||||
case 2: // Hardware SPI2 (FSPI)
|
|
||||||
esp.SYSTEM.SetPERIP_CLK_EN0_SPI2_CLK_EN(1)
|
|
||||||
esp.SYSTEM.SetPERIP_RST_EN0_SPI2_RST(1)
|
|
||||||
esp.SYSTEM.SetPERIP_RST_EN0_SPI2_RST(0)
|
|
||||||
case 3: // Hardware SPI3 (HSPI)
|
|
||||||
esp.SYSTEM.SetPERIP_CLK_EN0_SPI3_CLK_EN(1)
|
|
||||||
esp.SYSTEM.SetPERIP_RST_EN0_SPI3_RST(1)
|
|
||||||
esp.SYSTEM.SetPERIP_RST_EN0_SPI3_RST(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get bus handle - both SPI2 and SPI3 use SPI2_Type
|
|
||||||
bus, ok := spi.Bus.(*esp.SPI2_Type)
|
|
||||||
if !ok {
|
|
||||||
return ErrInvalidSPIBus
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset timing: cs_setup_time = 0, cs_hold_time = 0
|
|
||||||
bus.USER1.Set(0)
|
|
||||||
|
|
||||||
// Use all 64 bytes of the buffer
|
|
||||||
bus.SetUSER_USR_MISO_HIGHPART(0)
|
|
||||||
bus.SetUSER_USR_MOSI_HIGHPART(0)
|
|
||||||
|
|
||||||
// Disable unneeded interrupts and clear all USER bits first
|
|
||||||
bus.SLAVE.Set(0)
|
|
||||||
bus.USER.Set(0)
|
|
||||||
|
|
||||||
// Clear other important registers like ESP32-C3
|
|
||||||
bus.MISC.Set(0)
|
|
||||||
bus.CTRL.Set(0)
|
|
||||||
bus.CLOCK.Set(0)
|
|
||||||
|
|
||||||
// Clear data buffers like ESP32-C3
|
|
||||||
bus.W0.Set(0)
|
|
||||||
bus.W1.Set(0)
|
|
||||||
bus.W2.Set(0)
|
|
||||||
bus.W3.Set(0)
|
|
||||||
|
|
||||||
// Configure master clock gate - CRITICAL: need CLK_EN bit!
|
|
||||||
bus.SetCLK_GATE_CLK_EN(1) // Enable basic SPI clock (bit 0)
|
|
||||||
bus.SetCLK_GATE_MST_CLK_ACTIVE(1) // Enable master clock (bit 1)
|
|
||||||
bus.SetCLK_GATE_MST_CLK_SEL(1) // Select master clock (bit 2)
|
|
||||||
|
|
||||||
// Configure DMA following ESP-IDF HAL
|
|
||||||
// Reset DMA configuration
|
|
||||||
bus.DMA_CONF.Set(0)
|
|
||||||
// Set DMA segment transaction clear enable bits
|
|
||||||
bus.SetDMA_CONF_SLV_TX_SEG_TRANS_CLR_EN(1)
|
|
||||||
bus.SetDMA_CONF_SLV_RX_SEG_TRANS_CLR_EN(1)
|
|
||||||
// dma_seg_trans_en = 0 (already 0 from DMA_CONF.Set(0))
|
|
||||||
|
|
||||||
// Configure master mode
|
|
||||||
bus.SetUSER_USR_MOSI(1) // Enable MOSI
|
|
||||||
bus.SetUSER_USR_MISO(1) // Enable MISO
|
|
||||||
bus.SetUSER_DOUTDIN(1) // Full-duplex mode
|
|
||||||
bus.SetCTRL_WR_BIT_ORDER(0) // MSB first
|
|
||||||
bus.SetCTRL_RD_BIT_ORDER(0) // MSB first
|
|
||||||
|
|
||||||
// CRITICAL: Enable clock output (from working test)
|
|
||||||
bus.SetMISC_CK_DIS(0) // Enable CLK output - THIS IS KEY!
|
|
||||||
|
|
||||||
// Configure SPI mode (CPOL/CPHA) following ESP-IDF HAL
|
|
||||||
switch config.Mode {
|
|
||||||
case SPI_MODE0:
|
|
||||||
// CPOL=0, CPHA=0 (default)
|
|
||||||
case SPI_MODE1:
|
|
||||||
bus.SetUSER_CK_OUT_EDGE(1) // CPHA=1
|
|
||||||
case SPI_MODE2:
|
|
||||||
bus.SetMISC_CK_IDLE_EDGE(1) // CPOL=1
|
|
||||||
bus.SetUSER_CK_OUT_EDGE(1) // CPHA=1
|
|
||||||
case SPI_MODE3:
|
|
||||||
bus.SetMISC_CK_IDLE_EDGE(1) // CPOL=1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure SPI bus clock using ESP32-C3 algorithm for better accuracy
|
|
||||||
bus.CLOCK.Set(freqToClockDiv(config.Frequency))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer writes/reads a single byte using the SPI interface.
|
|
||||||
// Implementation following ESP-IDF HAL spi_ll_user_start with proper USER register setup
|
|
||||||
func (spi *SPI) Transfer(w byte) (byte, error) {
|
|
||||||
// Both SPI2 and SPI3 use SPI2_Type
|
|
||||||
bus, ok := spi.Bus.(*esp.SPI2_Type)
|
|
||||||
if !ok {
|
|
||||||
return 0, errors.New("invalid SPI bus type")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set transfer length (8 bits = 7 in register)
|
|
||||||
bus.SetMS_DLEN_MS_DATA_BITLEN(7)
|
|
||||||
|
|
||||||
// Clear any pending interrupt flags BEFORE starting transaction
|
|
||||||
bus.SetDMA_INT_CLR_TRANS_DONE_INT_CLR(1)
|
|
||||||
|
|
||||||
// Write data to buffer (use W0 register)
|
|
||||||
bus.W0.Set(uint32(w))
|
|
||||||
|
|
||||||
// CRITICAL: Apply configuration before transmission (like ESP-IDF spi_ll_apply_config)
|
|
||||||
bus.SetCMD_UPDATE(1)
|
|
||||||
for bus.GetCMD_UPDATE() != 0 {
|
|
||||||
// Wait for config to be applied
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start transaction following ESP-IDF HAL spi_ll_user_start
|
|
||||||
bus.SetCMD_USR(1)
|
|
||||||
|
|
||||||
// Wait for completion using CMD_USR flag (like ESP32-C3 approach)
|
|
||||||
// Hardware clears CMD_USR when transaction is complete
|
|
||||||
timeout := 100000
|
|
||||||
for bus.GetCMD_USR() != 0 && timeout > 0 {
|
|
||||||
timeout--
|
|
||||||
// Wait for CMD_USR to be cleared by hardware
|
|
||||||
}
|
|
||||||
|
|
||||||
if timeout == 0 {
|
|
||||||
return 0, errors.New("SPI transfer timeout")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read received data from W0 register
|
|
||||||
result := byte(bus.W0.Get() & 0xFF)
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tx handles read/write operation for SPI interface. Since SPI is a synchronous write/read
|
|
||||||
// interface, there must always be the same number of bytes written as bytes read.
|
|
||||||
// This is accomplished by sending zero bits if r is bigger than w or discarding
|
|
||||||
// the incoming data if w is bigger than r.
|
|
||||||
// Optimized implementation ported from ESP32-C3 for better performance.
|
|
||||||
func (spi *SPI) Tx(w, r []byte) error {
|
|
||||||
toTransfer := len(w)
|
|
||||||
if len(r) > toTransfer {
|
|
||||||
toTransfer = len(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get bus handle - both SPI2 and SPI3 use SPI2_Type
|
|
||||||
bus, ok := spi.Bus.(*esp.SPI2_Type)
|
|
||||||
if !ok {
|
|
||||||
return ErrInvalidSPIBus
|
|
||||||
}
|
|
||||||
|
|
||||||
for toTransfer > 0 {
|
|
||||||
// Chunk 64 bytes at a time.
|
|
||||||
chunkSize := toTransfer
|
|
||||||
if chunkSize > 64 {
|
|
||||||
chunkSize = 64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill tx buffer.
|
|
||||||
transferWords := (*[16]volatile.Register32)(unsafe.Add(unsafe.Pointer(&bus.W0), 0))
|
|
||||||
if len(w) >= 64 {
|
|
||||||
// We can fill the entire 64-byte transfer buffer with data.
|
|
||||||
// This loop is slightly faster than the loop below.
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
word := uint32(w[i*4]) | uint32(w[i*4+1])<<8 | uint32(w[i*4+2])<<16 | uint32(w[i*4+3])<<24
|
|
||||||
transferWords[i].Set(word)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We can't fill the entire transfer buffer, so we need to be a bit
|
|
||||||
// more careful.
|
|
||||||
// Note that parts of the transfer buffer that aren't used still
|
|
||||||
// need to be set to zero, otherwise we might be transferring
|
|
||||||
// garbage from a previous transmission if w is smaller than r.
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
var word uint32
|
|
||||||
if i*4+3 < len(w) {
|
|
||||||
word |= uint32(w[i*4+3]) << 24
|
|
||||||
}
|
|
||||||
if i*4+2 < len(w) {
|
|
||||||
word |= uint32(w[i*4+2]) << 16
|
|
||||||
}
|
|
||||||
if i*4+1 < len(w) {
|
|
||||||
word |= uint32(w[i*4+1]) << 8
|
|
||||||
}
|
|
||||||
if i*4+0 < len(w) {
|
|
||||||
word |= uint32(w[i*4+0]) << 0
|
|
||||||
}
|
|
||||||
transferWords[i].Set(word)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the transfer.
|
|
||||||
bus.SetMS_DLEN_MS_DATA_BITLEN(uint32(chunkSize)*8 - 1)
|
|
||||||
|
|
||||||
bus.SetCMD_UPDATE(1)
|
|
||||||
for bus.GetCMD_UPDATE() != 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
bus.SetCMD_USR(1)
|
|
||||||
for bus.GetCMD_USR() != 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read rx buffer.
|
|
||||||
rxSize := chunkSize
|
|
||||||
if rxSize > len(r) {
|
|
||||||
rxSize = len(r)
|
|
||||||
}
|
|
||||||
for i := 0; i < rxSize; i++ {
|
|
||||||
r[i] = byte(transferWords[i/4].Get() >> ((i % 4) * 8))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cut off some part of the output buffer so the next iteration we will
|
|
||||||
// only send the remaining bytes.
|
|
||||||
if len(w) < chunkSize {
|
|
||||||
w = nil
|
|
||||||
} else {
|
|
||||||
w = w[chunkSize:]
|
|
||||||
}
|
|
||||||
if len(r) < chunkSize {
|
|
||||||
r = nil
|
|
||||||
} else {
|
|
||||||
r = r[chunkSize:]
|
|
||||||
}
|
|
||||||
toTransfer -= chunkSize
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the SPI bus frequency from the APB clock frequency.
|
|
||||||
// Note: APB clock is always 80MHz on ESP32-S3, independent of CPU frequency.
|
|
||||||
// Ported from ESP32-C3 implementation for better accuracy.
|
|
||||||
func freqToClockDiv(hz uint32) uint32 {
|
|
||||||
// Use APB clock frequency (80MHz), not CPU frequency!
|
|
||||||
// SPI peripheral is connected to APB bus which stays at 80MHz
|
|
||||||
const apbFreq = pplClockFreq // 80MHz
|
|
||||||
|
|
||||||
if hz >= apbFreq { // maximum frequency
|
|
||||||
return 1 << 31
|
|
||||||
}
|
|
||||||
if hz < (apbFreq / (16 * 64)) { // minimum frequency
|
|
||||||
return 15<<18 | 63<<12 | 31<<6 | 63 // pre=15, n=63
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate looking for an exact match
|
|
||||||
// or iterate all 16 prescaler options
|
|
||||||
// looking for the smallest error
|
|
||||||
var bestPre, bestN, bestErr uint32
|
|
||||||
bestN = 1
|
|
||||||
bestErr = 0xffffffff
|
|
||||||
q := uint32(float32(apbFreq)/float32(hz) + float32(0.5))
|
|
||||||
for p := uint32(0); p < 16; p++ {
|
|
||||||
n := q/(p+1) - 1
|
|
||||||
if n < 1 { // prescaler became too large, stop enum
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if n > 63 { // prescaler too small, skip to next
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
freq := apbFreq / ((p + 1) * (n + 1))
|
|
||||||
if freq == hz { // exact match
|
|
||||||
return p<<18 | n<<12 | (n/2)<<6 | n
|
|
||||||
}
|
|
||||||
|
|
||||||
var err uint32
|
|
||||||
if freq < hz {
|
|
||||||
err = hz - freq
|
|
||||||
} else {
|
|
||||||
err = freq - hz
|
|
||||||
}
|
|
||||||
if err < bestErr {
|
|
||||||
bestErr = err
|
|
||||||
bestPre = p
|
|
||||||
bestN = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestPre<<18 | bestN<<12 | (bestN/2)<<6 | bestN
|
|
||||||
}
|
|
||||||
|
|
||||||
// isDefaultSPIPins checks if the given pins match the default SPI pin configuration
|
|
||||||
// that supports IO MUX direct connection for better performance
|
|
||||||
func isDefaultSPIPins(busID uint8, config SPIConfig) bool {
|
|
||||||
switch busID {
|
|
||||||
case 2: // SPI2 (FSPI)
|
|
||||||
return config.SCK == SPI1_SCK_PIN &&
|
|
||||||
config.SDO == SPI1_MOSI_PIN &&
|
|
||||||
config.SDI == SPI1_MISO_PIN &&
|
|
||||||
(config.CS == SPI1_CS_PIN || config.CS == NoPin)
|
|
||||||
case 3: // SPI3 (HSPI)
|
|
||||||
return config.SCK == SPI2_SCK_PIN &&
|
|
||||||
config.SDO == SPI2_MOSI_PIN &&
|
|
||||||
config.SDI == SPI2_MISO_PIN &&
|
|
||||||
(config.CS == SPI2_CS_PIN || config.CS == NoPin)
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
//go:build esp32s3 || esp32c3
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/esp"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// USB Serial/JTAG Controller
|
|
||||||
// See esp32-c3_technical_reference_manual_en.pdf
|
|
||||||
// pg. 736
|
|
||||||
type USB_DEVICE struct {
|
|
||||||
Bus *esp.USB_DEVICE_Type
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_USBCDC = &USB_DEVICE{
|
|
||||||
Bus: esp.USB_DEVICE,
|
|
||||||
}
|
|
||||||
|
|
||||||
USBCDC Serialer = _USBCDC
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errUSBWrongSize = errors.New("USB: invalid write size")
|
|
||||||
errUSBCouldNotWriteAllData = errors.New("USB: could not write all data")
|
|
||||||
errUSBBufferEmpty = errors.New("USB: read buffer empty")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Serialer interface {
|
|
||||||
WriteByte(c byte) error
|
|
||||||
Write(data []byte) (n int, err error)
|
|
||||||
Configure(config UARTConfig) error
|
|
||||||
Buffered() int
|
|
||||||
ReadByte() (byte, error)
|
|
||||||
DTR() bool
|
|
||||||
RTS() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func initUSB() {}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) Configure(config UARTConfig) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) WriteByte(c byte) error {
|
|
||||||
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
|
||||||
return errUSBCouldNotWriteAllData
|
|
||||||
}
|
|
||||||
|
|
||||||
usbdev.Bus.SetEP1_RDWR_BYTE(uint32(c))
|
|
||||||
usbdev.flush()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) Write(data []byte) (n int, err error) {
|
|
||||||
if len(data) == 0 || len(data) > 64 {
|
|
||||||
return 0, errUSBWrongSize
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, c := range data {
|
|
||||||
if usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
|
||||||
if i > 0 {
|
|
||||||
usbdev.flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
return i, errUSBCouldNotWriteAllData
|
|
||||||
}
|
|
||||||
usbdev.Bus.SetEP1_RDWR_BYTE(uint32(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
usbdev.flush()
|
|
||||||
return len(data), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) Buffered() int {
|
|
||||||
return int(usbdev.Bus.GetEP1_CONF_SERIAL_OUT_EP_DATA_AVAIL())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) ReadByte() (byte, error) {
|
|
||||||
if usbdev.Bus.GetEP1_CONF_SERIAL_OUT_EP_DATA_AVAIL() != 0 {
|
|
||||||
return byte(usbdev.Bus.GetEP1_RDWR_BYTE()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) DTR() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) RTS() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (usbdev *USB_DEVICE) flush() {
|
|
||||||
usbdev.Bus.SetEP1_CONF_WR_DONE(1)
|
|
||||||
for usbdev.Bus.GetEP1_CONF_SERIAL_IN_EP_DATA_FREE() == 0 {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,9 +3,7 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
"errors"
|
|
||||||
"internal/binary"
|
"internal/binary"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@@ -382,10 +380,6 @@ func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
|||||||
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
||||||
// This method assumes that the destination is already erased.
|
// This method assumes that the destination is already erased.
|
||||||
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, nil // nothing to do (and it would fail in sd_flash_write)
|
|
||||||
}
|
|
||||||
|
|
||||||
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
|
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
|
||||||
return 0, errFlashCannotWritePastEOF
|
return 0, errFlashCannotWritePastEOF
|
||||||
}
|
}
|
||||||
@@ -393,35 +387,6 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
|||||||
address := FlashDataStart() + uintptr(off)
|
address := FlashDataStart() + uintptr(off)
|
||||||
padded := flashPad(p, int(f.WriteBlockSize()))
|
padded := flashPad(p, int(f.WriteBlockSize()))
|
||||||
|
|
||||||
// When the SoftDevice is enabled, access to the flash is restricted and
|
|
||||||
// must go through the SoftDevice API.
|
|
||||||
if isSoftDeviceEnabled() {
|
|
||||||
// Call sd_flash_write, which is SVC_SOC_BASE + 9 in all the
|
|
||||||
// SoftDevices I've checked.
|
|
||||||
// Documentation:
|
|
||||||
// https://docs.nordicsemi.com/bundle/s140_v6.0.0_api/page/group_n_r_f_s_o_c_f_u_n_c_t_i_o_n_s.html
|
|
||||||
numberOfWords := len(padded) / 4 // flash access goes in 32-bit words
|
|
||||||
result := arm.SVCall3(0x20+9, address, &padded[0], uint32(numberOfWords))
|
|
||||||
if result != 0 {
|
|
||||||
// Could not queue flash operation? Not sure when this can
|
|
||||||
// happen.
|
|
||||||
return 0, flashError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until the SoftDevice is finished.
|
|
||||||
flashStatus = flashStatusBusy
|
|
||||||
for flashStatus == flashStatusBusy {
|
|
||||||
handleSoftDeviceEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the operation was successful.
|
|
||||||
if flashStatus != flashStatusOk {
|
|
||||||
flashStatus = flashStatusOk
|
|
||||||
return 0, flashError
|
|
||||||
}
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
waitWhileFlashBusy()
|
waitWhileFlashBusy()
|
||||||
|
|
||||||
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
|
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
|
||||||
@@ -463,40 +428,6 @@ func (f flashBlockDevice) EraseBlockSize() int64 {
|
|||||||
// supports this. The start and len parameters are in block numbers, use
|
// supports this. The start and len parameters are in block numbers, use
|
||||||
// EraseBlockSize to map addresses to blocks.
|
// EraseBlockSize to map addresses to blocks.
|
||||||
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
|
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
|
||||||
// When the SoftDevice is enabled, access to the flash is restricted and
|
|
||||||
// must go through the SoftDevice API.
|
|
||||||
if isSoftDeviceEnabled() {
|
|
||||||
for i := range uint32(len) {
|
|
||||||
flashPage := uint32(FlashDataStart())/eraseBlockSizeValue + uint32(start) + i
|
|
||||||
|
|
||||||
// Call sd_flash_page_erase, which is SVC_SOC_BASE + 8 in all the
|
|
||||||
// SoftDevices I've checked.
|
|
||||||
// Documentation:
|
|
||||||
// https://docs.nordicsemi.com/bundle/s140_v6.0.0_api/page/group_n_r_f_s_o_c_f_u_n_c_t_i_o_n_s.html#ga9c93dd94a138ad8b5ed3693ea38ffb3e
|
|
||||||
result := arm.SVCall1(0x20+8, flashPage)
|
|
||||||
if result != 0 {
|
|
||||||
// Could not queue flash operation? Not sure when this can
|
|
||||||
// happen.
|
|
||||||
return flashError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until the SoftDevice is finished.
|
|
||||||
flashStatus = flashStatusBusy
|
|
||||||
for flashStatus == flashStatusBusy {
|
|
||||||
handleSoftDeviceEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the operation was successful.
|
|
||||||
if flashStatus != flashStatusOk {
|
|
||||||
flashStatus = flashStatusOk
|
|
||||||
return flashError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SoftDevice is not used or enabled. Use NVIC directly.
|
|
||||||
|
|
||||||
address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
|
address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
|
||||||
waitWhileFlashBusy()
|
waitWhileFlashBusy()
|
||||||
|
|
||||||
@@ -516,52 +447,3 @@ func waitWhileFlashBusy() {
|
|||||||
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
|
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var flashError = errors.New("machine: flash operation failed")
|
|
||||||
|
|
||||||
const (
|
|
||||||
flashStatusOk = iota
|
|
||||||
flashStatusError
|
|
||||||
flashStatusBusy
|
|
||||||
)
|
|
||||||
|
|
||||||
var flashStatus uint8 = flashStatusOk
|
|
||||||
|
|
||||||
var sdEvent uint32
|
|
||||||
|
|
||||||
// Process all queued SoftDevice events. May only be called when the SoftDevice is enabled.
|
|
||||||
//
|
|
||||||
// Normally these are handled in the same interrupt where Bluetooth events are
|
|
||||||
// handled. But in TinyGo, that's complicated. One option would be to put it in
|
|
||||||
// the tinygo.org/x/bluetooth package, but that would cause a circular
|
|
||||||
// dependency between the machine and the bluetooth package. Another would be to
|
|
||||||
// put it here, but let the bluetooth package call handleSoftDeviceEvents, but
|
|
||||||
// that relies on updating the bluetooth package at the same time. As a
|
|
||||||
// compromise, these events are handled directly where they are expected (here
|
|
||||||
// in the machine package). This works in practice since there are only very few
|
|
||||||
// of such events (at the moment, only flash-related ones which is in the
|
|
||||||
// machine package anyway).
|
|
||||||
func handleSoftDeviceEvents() {
|
|
||||||
for {
|
|
||||||
var result uintptr
|
|
||||||
if nrf.Device == "nrf52" || nrf.Device == "nrf52840" || nrf.Device == "nrf52833" {
|
|
||||||
// sd_evt_get: SOC_SVC_BASE_NOT_AVAILABLE + 31
|
|
||||||
result = arm.SVCall1(0x2C+31, &sdEvent)
|
|
||||||
} else {
|
|
||||||
return // TODO: nrf51 etc
|
|
||||||
}
|
|
||||||
if result != 0 {
|
|
||||||
// Some error occured. The only possible error is
|
|
||||||
// NRF_ERROR_NOT_FOUND, which means there are no more events.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following events are the same numbers in all SoftDevices I've checked.
|
|
||||||
switch sdEvent {
|
|
||||||
case 2: // NRF_EVT_FLASH_OPERATION_SUCCESS
|
|
||||||
flashStatus = flashStatusOk
|
|
||||||
case 3: // NRF_EVT_FLASH_OPERATION_ERROR
|
|
||||||
flashStatus = flashStatusError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -69,5 +69,3 @@ const eraseBlockSizeValue = 4096
|
|||||||
func eraseBlockSize() int64 {
|
func eraseBlockSize() int64 {
|
||||||
return eraseBlockSizeValue
|
return eraseBlockSizeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const spiMaxBufferSize = 255 // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
|
||||||
|
|||||||
@@ -90,5 +90,3 @@ const eraseBlockSizeValue = 4096
|
|||||||
func eraseBlockSize() int64 {
|
func eraseBlockSize() int64 {
|
||||||
return eraseBlockSizeValue
|
return eraseBlockSizeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
|
||||||
|
|||||||
@@ -108,9 +108,3 @@ const eraseBlockSizeValue = 4096
|
|||||||
func eraseBlockSize() int64 {
|
func eraseBlockSize() int64 {
|
||||||
return eraseBlockSizeValue
|
return eraseBlockSizeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
|
||||||
|
|
||||||
// ADC instance for the VDDH input pin. This pin is typically connected to USB
|
|
||||||
// input voltage (~5V) or directly to a battery.
|
|
||||||
var ADC_VDDH = ADC{adcVDDHPin}
|
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ func initEndpoint(ep, config uint32) {
|
|||||||
|
|
||||||
// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
|
// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
|
||||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||||
sendUSBPacket(ep, data)
|
sendUSBPacket(ep, data, 0)
|
||||||
|
|
||||||
// clear transfer complete flag
|
// clear transfer complete flag
|
||||||
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
|
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
|
||||||
@@ -267,32 +267,33 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
|
|||||||
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
||||||
//
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func sendUSBPacket(ep uint32, data []byte) {
|
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||||
// Select the corresponding buffer.
|
|
||||||
count := len(data)
|
count := len(data)
|
||||||
var buffer []byte
|
if 0 < int(maxsize) && int(maxsize) < count {
|
||||||
|
count = int(maxsize)
|
||||||
|
}
|
||||||
|
|
||||||
if ep == 0 {
|
if ep == 0 {
|
||||||
buffer = udd_ep_control_cache_buffer[:]
|
copy(udd_ep_control_cache_buffer[:], data[:count])
|
||||||
if count > usb.EndpointPacketSize {
|
if count > usb.EndpointPacketSize {
|
||||||
// The packet must be sent in chunks.
|
|
||||||
sendOnEP0DATADONE.offset = usb.EndpointPacketSize
|
sendOnEP0DATADONE.offset = usb.EndpointPacketSize
|
||||||
sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[usb.EndpointPacketSize]
|
sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[sendOnEP0DATADONE.offset]
|
||||||
sendOnEP0DATADONE.count = count - usb.EndpointPacketSize
|
sendOnEP0DATADONE.count = count - usb.EndpointPacketSize
|
||||||
count = usb.EndpointPacketSize
|
count = usb.EndpointPacketSize
|
||||||
}
|
}
|
||||||
|
sendViaEPIn(
|
||||||
|
ep,
|
||||||
|
&udd_ep_control_cache_buffer[0],
|
||||||
|
count,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
buffer = udd_ep_in_cache_buffer[ep][:]
|
copy(udd_ep_in_cache_buffer[ep][:], data[:count])
|
||||||
|
sendViaEPIn(
|
||||||
|
ep,
|
||||||
|
&udd_ep_in_cache_buffer[ep][0],
|
||||||
|
count,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the packet to the buffer.
|
|
||||||
copy(buffer[:len(data)], data)
|
|
||||||
|
|
||||||
// Send the first chunk of the packet.
|
|
||||||
sendViaEPIn(
|
|
||||||
ep,
|
|
||||||
&buffer[0],
|
|
||||||
count,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEndpointRx(ep uint32) []byte {
|
func handleEndpointRx(ep uint32) []byte {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
|||||||
|
|
||||||
// Configure for a single shot to perform both write and read (as applicable)
|
// Configure for a single shot to perform both write and read (as applicable)
|
||||||
if len(w) != 0 {
|
if len(w) != 0 {
|
||||||
i2c.Bus.TXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(w)))))
|
i2c.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
|
||||||
i2c.Bus.TXD.MAXCNT.Set(uint32(len(w)))
|
i2c.Bus.TXD.MAXCNT.Set(uint32(len(w)))
|
||||||
|
|
||||||
// If no read, immediately signal stop after TX
|
// If no read, immediately signal stop after TX
|
||||||
@@ -58,7 +58,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(r) != 0 {
|
if len(r) != 0 {
|
||||||
i2c.Bus.RXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(r)))))
|
i2c.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
|
||||||
i2c.Bus.RXD.MAXCNT.Set(uint32(len(r)))
|
i2c.Bus.RXD.MAXCNT.Set(uint32(len(r)))
|
||||||
|
|
||||||
// Auto-start Rx after Tx and Stop after Rx
|
// Auto-start Rx after Tx and Stop after Rx
|
||||||
@@ -89,11 +89,6 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the w and r buffers stay alive until this point, so they won't
|
|
||||||
// be garbage collected while the buffers are used by the hardware.
|
|
||||||
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(w)))
|
|
||||||
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(r)))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +117,7 @@ func (i2c *I2C) Listen(addr uint8) error {
|
|||||||
//
|
//
|
||||||
// For request events, the caller MUST call `Reply` to avoid hanging the i2c bus indefinitely.
|
// For request events, the caller MUST call `Reply` to avoid hanging the i2c bus indefinitely.
|
||||||
func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err error) {
|
func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err error) {
|
||||||
i2c.BusT.RXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(buf)))))
|
i2c.BusT.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
|
||||||
i2c.BusT.RXD.MAXCNT.Set(uint32(len(buf)))
|
i2c.BusT.RXD.MAXCNT.Set(uint32(len(buf)))
|
||||||
|
|
||||||
i2c.BusT.TASKS_PREPARERX.Set(nrf.TWIS_TASKS_PREPARERX_TASKS_PREPARERX_Trigger)
|
i2c.BusT.TASKS_PREPARERX.Set(nrf.TWIS_TASKS_PREPARERX_TASKS_PREPARERX_Trigger)
|
||||||
@@ -139,10 +134,6 @@ func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure buf stays alive until this point, so it won't be garbage
|
|
||||||
// collected while it is used by the hardware.
|
|
||||||
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(buf)))
|
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
evt = I2CFinish
|
evt = I2CFinish
|
||||||
err = nil
|
err = nil
|
||||||
@@ -172,7 +163,7 @@ func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err err
|
|||||||
|
|
||||||
// Reply supplies the response data the controller.
|
// Reply supplies the response data the controller.
|
||||||
func (i2c *I2C) Reply(buf []byte) error {
|
func (i2c *I2C) Reply(buf []byte) error {
|
||||||
i2c.BusT.TXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(buf)))))
|
i2c.BusT.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
|
||||||
i2c.BusT.TXD.MAXCNT.Set(uint32(len(buf)))
|
i2c.BusT.TXD.MAXCNT.Set(uint32(len(buf)))
|
||||||
|
|
||||||
i2c.BusT.EVENTS_STOPPED.Set(0)
|
i2c.BusT.EVENTS_STOPPED.Set(0)
|
||||||
@@ -189,10 +180,6 @@ func (i2c *I2C) Reply(buf []byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the buffer stays alive until this point, so it won't be garbage
|
|
||||||
// collected while it is used by the hardware.
|
|
||||||
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(buf)))
|
|
||||||
|
|
||||||
i2c.BusT.EVENTS_STOPPED.Set(0)
|
i2c.BusT.EVENTS_STOPPED.Set(0)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ func CPUFrequency() uint32 {
|
|||||||
return 64000000
|
return 64000000
|
||||||
}
|
}
|
||||||
|
|
||||||
var adcVDDHPin = Pin(254) // special pin number for VDDH on the nrf52840
|
|
||||||
|
|
||||||
// InitADC initializes the registers needed for ADC.
|
// InitADC initializes the registers needed for ADC.
|
||||||
func InitADC() {
|
func InitADC() {
|
||||||
// Enable ADC.
|
// Enable ADC.
|
||||||
@@ -28,7 +26,7 @@ func InitADC() {
|
|||||||
// Samples can be 1(default), 2, 4, 8, 16, 32, 64, 128, 256 samples
|
// Samples can be 1(default), 2, 4, 8, 16, 32, 64, 128, 256 samples
|
||||||
func (a *ADC) Configure(config ADCConfig) {
|
func (a *ADC) Configure(config ADCConfig) {
|
||||||
var configVal uint32 = nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESP_Pos |
|
var configVal uint32 = nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESP_Pos |
|
||||||
nrf.SAADC_CH_CONFIG_RESN_Bypass<<nrf.SAADC_CH_CONFIG_RESN_Pos |
|
nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESN_Pos |
|
||||||
nrf.SAADC_CH_CONFIG_REFSEL_Internal<<nrf.SAADC_CH_CONFIG_REFSEL_Pos |
|
nrf.SAADC_CH_CONFIG_REFSEL_Internal<<nrf.SAADC_CH_CONFIG_REFSEL_Pos |
|
||||||
nrf.SAADC_CH_CONFIG_MODE_SE<<nrf.SAADC_CH_CONFIG_MODE_Pos
|
nrf.SAADC_CH_CONFIG_MODE_SE<<nrf.SAADC_CH_CONFIG_MODE_Pos
|
||||||
|
|
||||||
@@ -118,43 +116,36 @@ func (a *ADC) Configure(config ADCConfig) {
|
|||||||
|
|
||||||
// Get returns the current value of an ADC pin in the range 0..0xffff.
|
// Get returns the current value of an ADC pin in the range 0..0xffff.
|
||||||
func (a *ADC) Get() uint16 {
|
func (a *ADC) Get() uint16 {
|
||||||
var adcPin uint32
|
var pwmPin uint32
|
||||||
var rawValue volatile.Register16
|
var rawValue volatile.Register16
|
||||||
|
|
||||||
switch a.Pin {
|
switch a.Pin {
|
||||||
case 2:
|
case 2:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0
|
||||||
case 3:
|
case 3:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1
|
||||||
case 4:
|
case 4:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2
|
||||||
case 5:
|
case 5:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3
|
||||||
case 28:
|
case 28:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4
|
||||||
case 29:
|
case 29:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5
|
||||||
case 30:
|
case 30:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6
|
||||||
case 31:
|
case 31:
|
||||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7
|
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7
|
||||||
case adcVDDHPin:
|
|
||||||
if Device == "nrf52840" {
|
|
||||||
adcPin = 0x0D // VDDHDIV5 on the nrf52840
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set pin to read.
|
// Set pin to read.
|
||||||
nrf.SAADC.CH[0].PSELP.Set(adcPin)
|
nrf.SAADC.CH[0].PSELN.Set(pwmPin)
|
||||||
|
nrf.SAADC.CH[0].PSELP.Set(pwmPin)
|
||||||
|
|
||||||
// Destination for sample result.
|
// Destination for sample result.
|
||||||
// Note: rawValue doesn't need to be kept alive for the GC, since the
|
nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&rawValue))))
|
||||||
// volatile read later will force it to stay alive.
|
|
||||||
nrf.SAADC.RESULT.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(&rawValue))))
|
|
||||||
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
|
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
|
||||||
|
|
||||||
// Start tasks.
|
// Start tasks.
|
||||||
@@ -310,18 +301,20 @@ func (spi *SPI) Transfer(w byte) (byte, error) {
|
|||||||
// padded until they fit: if len(w) > len(r) the extra bytes received will be
|
// padded until they fit: if len(w) > len(r) the extra bytes received will be
|
||||||
// dropped and if len(w) < len(r) extra 0 bytes will be sent.
|
// dropped and if len(w) < len(r) extra 0 bytes will be sent.
|
||||||
func (spi *SPI) Tx(w, r []byte) error {
|
func (spi *SPI) Tx(w, r []byte) error {
|
||||||
// Unfortunately the hardware (on the nrf52832) only supports a limited
|
// Unfortunately the hardware (on the nrf52832) only supports up to 255
|
||||||
// amount of bytes in the buffers (depending on the chip), so if either w or
|
// bytes in the buffers, so if either w or r is longer than that the
|
||||||
// r is longer than that the transfer needs to be broken up in pieces.
|
// transfer needs to be broken up in pieces.
|
||||||
|
// The nrf52840 supports far larger buffers however, which isn't yet
|
||||||
|
// supported.
|
||||||
for len(r) != 0 || len(w) != 0 {
|
for len(r) != 0 || len(w) != 0 {
|
||||||
// Prepare the SPI transfer: set the DMA pointers and lengths.
|
// Prepare the SPI transfer: set the DMA pointers and lengths.
|
||||||
// read buffer
|
// read buffer
|
||||||
nr := uint32(len(r))
|
nr := uint32(len(r))
|
||||||
if nr > 0 {
|
if nr > 0 {
|
||||||
if nr > spiMaxBufferSize {
|
if nr > 255 {
|
||||||
nr = spiMaxBufferSize
|
nr = 255
|
||||||
}
|
}
|
||||||
spi.Bus.RXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(r)))))
|
spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
|
||||||
r = r[nr:]
|
r = r[nr:]
|
||||||
}
|
}
|
||||||
spi.Bus.RXD.MAXCNT.Set(nr)
|
spi.Bus.RXD.MAXCNT.Set(nr)
|
||||||
@@ -329,10 +322,10 @@ func (spi *SPI) Tx(w, r []byte) error {
|
|||||||
// write buffer
|
// write buffer
|
||||||
nw := uint32(len(w))
|
nw := uint32(len(w))
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
if nw > spiMaxBufferSize {
|
if nw > 255 {
|
||||||
nw = spiMaxBufferSize
|
nw = 255
|
||||||
}
|
}
|
||||||
spi.Bus.TXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(w)))))
|
spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
|
||||||
w = w[nw:]
|
w = w[nw:]
|
||||||
}
|
}
|
||||||
spi.Bus.TXD.MAXCNT.Set(nw)
|
spi.Bus.TXD.MAXCNT.Set(nw)
|
||||||
@@ -342,16 +335,10 @@ func (spi *SPI) Tx(w, r []byte) error {
|
|||||||
// finished if the transfer is send-only (a common case).
|
// finished if the transfer is send-only (a common case).
|
||||||
spi.Bus.TASKS_START.Set(1)
|
spi.Bus.TASKS_START.Set(1)
|
||||||
for spi.Bus.EVENTS_END.Get() == 0 {
|
for spi.Bus.EVENTS_END.Get() == 0 {
|
||||||
gosched()
|
|
||||||
}
|
}
|
||||||
spi.Bus.EVENTS_END.Set(0)
|
spi.Bus.EVENTS_END.Set(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the w and r buffers stay alive for the GC until this point,
|
|
||||||
// since they are used by the hardware but not otherwise visible.
|
|
||||||
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(r)))
|
|
||||||
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(w)))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,3 @@ package machine
|
|||||||
func GetRNG() (ret uint32, err error) {
|
func GetRNG() (ret uint32, err error) {
|
||||||
return getRNG()
|
return getRNG()
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSoftDeviceEnabled() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import (
|
|||||||
|
|
||||||
// avoid a heap allocation in GetRNG.
|
// avoid a heap allocation in GetRNG.
|
||||||
var (
|
var (
|
||||||
bytesAvailable uint8
|
softdeviceEnabled uint8
|
||||||
buf [4]uint8
|
bytesAvailable uint8
|
||||||
|
buf [4]uint8
|
||||||
|
|
||||||
errNoSoftDeviceSupport = errors.New("rng: softdevice not supported on this device")
|
errNoSoftDeviceSupport = errors.New("rng: softdevice not supported on this device")
|
||||||
errNotEnoughRandomData = errors.New("rng: not enough random data available")
|
errNotEnoughRandomData = errors.New("rng: not enough random data available")
|
||||||
@@ -23,7 +24,9 @@ var (
|
|||||||
func GetRNG() (ret uint32, err error) {
|
func GetRNG() (ret uint32, err error) {
|
||||||
// First check whether the SoftDevice is enabled.
|
// First check whether the SoftDevice is enabled.
|
||||||
// sd_rand_application_bytes_available_get cannot be called when the SoftDevice is not enabled.
|
// sd_rand_application_bytes_available_get cannot be called when the SoftDevice is not enabled.
|
||||||
if !isSoftDeviceEnabled() {
|
arm.SVCall1(0x12, &softdeviceEnabled) // sd_softdevice_is_enabled
|
||||||
|
|
||||||
|
if softdeviceEnabled == 0 {
|
||||||
return getRNG()
|
return getRNG()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +57,3 @@ func GetRNG() (ret uint32, err error) {
|
|||||||
|
|
||||||
return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24, nil
|
return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is defined in the runtime, but we need it too.
|
|
||||||
//
|
|
||||||
//go:linkname isSoftDeviceEnabled runtime.isSoftDeviceEnabled
|
|
||||||
func isSoftDeviceEnabled() bool
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
|||||||
const ackTimeout = 570
|
const ackTimeout = 570
|
||||||
|
|
||||||
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC)
|
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC)
|
||||||
sendUSBPacket(0, []byte{})
|
sendUSBPacket(0, []byte{}, 0)
|
||||||
|
|
||||||
// Wait for transfer to complete with a timeout.
|
// Wait for transfer to complete with a timeout.
|
||||||
t := timer.timeElapsed()
|
t := timer.timeElapsed()
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
|||||||
const ackTimeout = 570
|
const ackTimeout = 570
|
||||||
|
|
||||||
rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_ACK_REC)
|
rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_ACK_REC)
|
||||||
sendUSBPacket(0, []byte{})
|
sendUSBPacket(0, []byte{}, 0)
|
||||||
|
|
||||||
// Wait for transfer to complete with a timeout.
|
// Wait for transfer to complete with a timeout.
|
||||||
t := timer.timeElapsed()
|
t := timer.timeElapsed()
|
||||||
|
|||||||
@@ -124,31 +124,6 @@ const (
|
|||||||
fnXIP pinFunc = 0
|
fnXIP pinFunc = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// validPins confirms that the SPI pin selection is a legitimate one
|
|
||||||
// for the the 2040 chip.
|
|
||||||
func (spi *SPI) validPins(config SPIConfig) error {
|
|
||||||
var okSDI, okSDO, okSCK bool
|
|
||||||
switch spi.Bus {
|
|
||||||
case rp.SPI0:
|
|
||||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
|
||||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
|
||||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
|
||||||
case rp.SPI1:
|
|
||||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
|
||||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
|
||||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case !okSDI:
|
|
||||||
return errSPIInvalidSDI
|
|
||||||
case !okSDO:
|
|
||||||
return errSPIInvalidSDO
|
|
||||||
case !okSCK:
|
|
||||||
return errSPIInvalidSCK
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure configures the gpio pin as per mode.
|
// Configure configures the gpio pin as per mode.
|
||||||
func (p Pin) Configure(config PinConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
if p == NoPin {
|
if p == NoPin {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/rp"
|
|
||||||
|
|
||||||
// Analog pins on RP2350a.
|
// Analog pins on RP2350a.
|
||||||
const (
|
const (
|
||||||
ADC0 Pin = GPIO26
|
ADC0 Pin = GPIO26
|
||||||
@@ -14,28 +12,3 @@ const (
|
|||||||
// fifth ADC channel.
|
// fifth ADC channel.
|
||||||
thermADC = 30
|
thermADC = 30
|
||||||
)
|
)
|
||||||
|
|
||||||
// validPins confirms that the SPI pin selection is a legitimate one
|
|
||||||
// for the the 2350a chip.
|
|
||||||
func (spi *SPI) validPins(config SPIConfig) error {
|
|
||||||
var okSDI, okSDO, okSCK bool
|
|
||||||
switch spi.Bus {
|
|
||||||
case rp.SPI0:
|
|
||||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
|
|
||||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
|
|
||||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
|
|
||||||
case rp.SPI1:
|
|
||||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
|
|
||||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
|
||||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case !okSDI:
|
|
||||||
return errSPIInvalidSDI
|
|
||||||
case !okSDO:
|
|
||||||
return errSPIInvalidSDO
|
|
||||||
case !okSCK:
|
|
||||||
return errSPIInvalidSCK
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/rp"
|
|
||||||
|
|
||||||
// RP2350B has additional pins.
|
// RP2350B has additional pins.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -48,28 +46,3 @@ var (
|
|||||||
PWM10 = getPWMGroup(10)
|
PWM10 = getPWMGroup(10)
|
||||||
PWM11 = getPWMGroup(11)
|
PWM11 = getPWMGroup(11)
|
||||||
)
|
)
|
||||||
|
|
||||||
// validPins confirms that the SPI pin selection is a legitimate one
|
|
||||||
// for the the 2350b chip.
|
|
||||||
func (spi *SPI) validPins(config SPIConfig) error {
|
|
||||||
var okSDI, okSDO, okSCK bool
|
|
||||||
switch spi.Bus {
|
|
||||||
case rp.SPI0:
|
|
||||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20 || config.SDI == 32 || config.SDI == 36
|
|
||||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23 || config.SDO == 35 || config.SDO == 39
|
|
||||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22 || config.SCK == 34 || config.SCK == 38
|
|
||||||
case rp.SPI1:
|
|
||||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28 || config.SDI == 40 || config.SDI == 44
|
|
||||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27 || config.SDO == 31 || config.SDO == 43 || config.SDO == 47
|
|
||||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26 || config.SCK == 30 || config.SCK == 42 || config.SCK == 46
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case !okSDI:
|
|
||||||
return errSPIInvalidSDI
|
|
||||||
case !okSDO:
|
|
||||||
return errSPIInvalidSDO
|
|
||||||
case !okSCK:
|
|
||||||
return errSPIInvalidSCK
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ var adcAref uint32
|
|||||||
|
|
||||||
// InitADC resets the ADC peripheral.
|
// InitADC resets the ADC peripheral.
|
||||||
func InitADC() {
|
func InitADC() {
|
||||||
resetBlock(rp.RESETS_RESET_ADC)
|
rp.RESETS.RESET.SetBits(rp.RESETS_RESET_ADC)
|
||||||
unresetBlockWait(rp.RESETS_RESET_ADC)
|
rp.RESETS.RESET.ClearBits(rp.RESETS_RESET_ADC)
|
||||||
|
for !rp.RESETS.RESET_DONE.HasBits(rp.RESETS_RESET_ADC) {
|
||||||
|
}
|
||||||
// enable ADC
|
// enable ADC
|
||||||
rp.ADC.CS.Set(rp.ADC_CS_EN)
|
rp.ADC.CS.Set(rp.ADC_CS_EN)
|
||||||
adcAref = 3300
|
adcAref = 3300
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user