mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-28 23:58:40 +00:00
Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c78cf05cd | |||
| e79edb58b2 | |||
| bf80e9b446 | |||
| 30355cec9f | |||
| 9111927f85 | |||
| 10224a4bf8 | |||
| 33ddee76db | |||
| 7fa50a55cf | |||
| 1c0c26b8b7 | |||
| 3abe58448b | |||
| 364ac2dcb2 | |||
| e111b489ec | |||
| f31b39775f | |||
| 6c30583062 | |||
| d29e8e5f22 | |||
| a93640662e | |||
| 3fb0a90854 | |||
| df09dbdf0f | |||
| 134de98ba5 | |||
| 221ea6a54c | |||
| 7ffbcbc666 | |||
| 5f66260c3a | |||
| ba95eb3e1b | |||
| f16697624d | |||
| cb311ed25e | |||
| 6f686d12af | |||
| 6a0acfc735 | |||
| 27f8ec17fc | |||
| a683881eb7 | |||
| 335b085fd2 | |||
| 11c2b76e29 | |||
| 6605b6e43f | |||
| ec18e89365 | |||
| 39cde9f9a3 | |||
| 2747027ef2 | |||
| 4465360f3d | |||
| c33113ab5f | |||
| eab43851ac | |||
| c3f1832d9a | |||
| 2328d1e799 | |||
| 5fabc203db | |||
| d9d19e812e | |||
| 45dd431f19 | |||
| 088a660c00 | |||
| 1ff348fb1f | |||
| cd364a9315 | |||
| ca584de84a | |||
| 594be6db63 | |||
| aef70a5839 | |||
| e6e7250a47 | |||
| 7b3dc19445 | |||
| 8e36d2758b | |||
| 7cbbfd6fc5 | |||
| e8bb38f7c9 | |||
| 1b9bb143bf | |||
| 41f666c670 | |||
| 469e2434fd | |||
| 278aa09819 | |||
| dded832238 | |||
| 8ffabbea64 | |||
| 29b4c6723f | |||
| 039f48f3d2 | |||
| eed4afda63 | |||
| ce888e1042 | |||
| f5d0ec93ef | |||
| 3a0d72457b | |||
| c980e48ad4 | |||
| f5bd750a4d | |||
| 0a5875ab2e | |||
| 54ecf74261 | |||
| 9b83acffe6 | |||
| 67aa15e92e | |||
| 0d6efc6429 | |||
| 1bd3ade825 | |||
| fb8ed8fde8 | |||
| c698eeacba | |||
| 906a20d3bf | |||
| 1be377c47d | |||
| 4dbb5dd4da | |||
| 997974376d | |||
| 7d51044892 | |||
| 37c080083b | |||
| 70a0dee8c8 | |||
| de83d67364 | |||
| 7a5a50d949 | |||
| d348d8b4cf | |||
| 443de9fe1c | |||
| c72f1d5e62 | |||
| ca8fcc9479 | |||
| d01a932201 | |||
| 85d223c5e2 | |||
| 7b486e398a | |||
| da69f18e91 | |||
| 6971b8a69e | |||
| 6682e41ab4 | |||
| 18033ebc36 | |||
| 89d9e33bca | |||
| e80e7e5c10 | |||
| 6233915ecf | |||
| fd8e0bd70b | |||
| 45e2863e9f | |||
| 4204f3d065 | |||
| ddbd65beec | |||
| 1a1506ef79 | |||
| 8793dc37fa | |||
| e9d78a78ea | |||
| 7ec9cfc61c | |||
| 294cf28e43 | |||
| 2f7a66e6d3 |
@@ -1,119 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
commands:
|
||||
submodules:
|
||||
steps:
|
||||
- run:
|
||||
name: "Pull submodules"
|
||||
command: git submodule update --init
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-19-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-19-v1
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/compiler-rt
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
hack-ninja-jobs:
|
||||
steps:
|
||||
- run:
|
||||
name: "Hack Ninja to use less jobs"
|
||||
command: |
|
||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
||||
chmod +x /go/bin/ninja
|
||||
build-binaryen-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-linux-v3
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
make binaryen
|
||||
- save_cache:
|
||||
key: binaryen-linux-v3
|
||||
paths:
|
||||
- build/wasm-opt
|
||||
test-linux:
|
||||
parameters:
|
||||
llvm:
|
||||
type: string
|
||||
fmt-check:
|
||||
type: boolean
|
||||
default: true
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y \
|
||||
llvm-<<parameters.llvm>>-dev \
|
||||
clang-<<parameters.llvm>> \
|
||||
libclang-<<parameters.llvm>>-dev \
|
||||
lld-<<parameters.llvm>> \
|
||||
cmake \
|
||||
ninja-build
|
||||
- hack-ninja-jobs
|
||||
- build-binaryen-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v4-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- when:
|
||||
condition: <<parameters.fmt-check>>
|
||||
steps:
|
||||
- run:
|
||||
# Do this before gen-device so that it doesn't check the
|
||||
# formatting of generated files.
|
||||
name: Check Go code formatting
|
||||
command: make fmt-check lint
|
||||
- run: make gen-device -j4
|
||||
# TODO: change this to -skip='TestErrors|TestWasm' with Go 1.20
|
||||
- run: go test -tags=llvm<<parameters.llvm>> -short -run='TestBuild|TestTest|TestGetList|TestTraceback'
|
||||
- run: make smoketest XTENSA=0
|
||||
- save_cache:
|
||||
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-oldest:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
docker:
|
||||
- image: golang:1.23-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "15"
|
||||
resource_class: large
|
||||
test-newest:
|
||||
# This tests the latest supported LLVM version when linking against system
|
||||
# libraries.
|
||||
docker:
|
||||
- image: golang:1.26-bookworm
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "20"
|
||||
resource_class: large
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-oldest
|
||||
# disable this test, since CircleCI seems unable to download due to rate-limits on Dockerhub.
|
||||
# - test-newest
|
||||
@@ -1,5 +1,4 @@
|
||||
build/
|
||||
llvm-*/
|
||||
.github
|
||||
.circleci
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# This CI job checks whether at least the smoke tests pass for the oldest
|
||||
# Go/LLVM version we claim to support.
|
||||
|
||||
name: Version compatibility test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-compat:
|
||||
runs-on: ubuntu-22.04 # this must be a specific version for the apt install below
|
||||
env:
|
||||
# Oldest versions currently supported by TinyGo
|
||||
LLVM: "15"
|
||||
Go: "1.24" # when updating this, also update minorMin in builder/config.go
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ env.Go }}
|
||||
cache: true
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ env.LLVM }} main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y \
|
||||
llvm-${{ env.LLVM }}-dev \
|
||||
clang-${{ env.LLVM }} \
|
||||
libclang-${{ env.LLVM }}-dev \
|
||||
lld-${{ env.LLVM }} \
|
||||
binaryen
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-linux-compat
|
||||
path: llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: llvm-project/compiler-rt
|
||||
- name: Go cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: go-build-${{ env.Go }}-llvm${{ env.LLVM }}-${{ hashFiles('go.sum') }}
|
||||
- name: Build TinyGo
|
||||
run: go install -tags=llvm${{ env.LLVM }}
|
||||
- run: tinygo version
|
||||
- run: make gen-device -j4
|
||||
- run: go test -tags=llvm${{ env.LLVM }} -short -skip=TestErrors
|
||||
- run: make smoketest XTENSA=0
|
||||
@@ -12,6 +12,24 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
go-mod-tidy:
|
||||
# Check that go.sum is up to date.
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Run go mod tidy
|
||||
run: go mod tidy
|
||||
- name: Check go.mod and go.sum are up to date
|
||||
run: git diff --exit-code
|
||||
|
||||
build-linux:
|
||||
# Build Linux binaries, ready for release.
|
||||
# This runs inside an Alpine Linux container so we can more easily create a
|
||||
@@ -142,7 +160,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
@@ -186,7 +204,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v6
|
||||
@@ -266,6 +284,8 @@ jobs:
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- run: make tinygo-test-baremetal
|
||||
- name: Check Go code formatting
|
||||
run: make fmt-check lint
|
||||
build-linux-cross:
|
||||
# Build ARM Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
@@ -303,7 +323,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
|
||||
@@ -17,12 +17,6 @@ jobs:
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
@@ -40,13 +34,13 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-20-windows-v1
|
||||
key: llvm-source-20-windows-v3
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -71,7 +65,7 @@ jobs:
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-20-windows-v2
|
||||
key: llvm-build-20-windows-v4
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -93,7 +87,7 @@ jobs:
|
||||
- name: Cache Go cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
key: go-cache-windows-v2-${{ hashFiles('go.mod') }}
|
||||
key: go-cache-windows-v3-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
C:/Users/runneradmin/AppData/Local/go-build
|
||||
C:/Users/runneradmin/go/pkg/mod
|
||||
@@ -124,12 +118,6 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
@@ -141,7 +129,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
@@ -157,18 +145,12 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
@@ -183,12 +165,6 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
@@ -200,7 +176,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26.2'
|
||||
go-version: '1.26.4'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
|
||||
+30
-10
@@ -194,6 +194,12 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm lld $(addprefix lib/lib,
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++17
|
||||
ifneq ($(uname),Windows_NT)
|
||||
# Disable GCC DWARF compression: lld built without zlib cannot link
|
||||
# object files with ELFCOMPRESS_ZLIB debug sections.
|
||||
CGO_CFLAGS+=-gz=none
|
||||
CGO_CXXFLAGS+=-gz=none
|
||||
endif
|
||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
|
||||
@@ -310,7 +316,7 @@ check-nodejs-version:
|
||||
|
||||
tinygo: ## Build the TinyGo compiler
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
|
||||
test: check-nodejs-version
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
|
||||
|
||||
@@ -379,22 +385,22 @@ TEST_PACKAGES_FAST = \
|
||||
# archive/zip requires os.ReadAt, which is not yet supported on windows
|
||||
# bytes requires mmap
|
||||
# compress/flate appears to hang on wasi
|
||||
# crypto/aes fails on wasi, needs panic()/recover()
|
||||
# crypto/aes needs reflect.Type.Method(), not yet implemented
|
||||
# crypto/des fails on wasi, needs panic()/recover()
|
||||
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
|
||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||
# encoding/xml takes a minute on linux and gives a stack overflow on wasi
|
||||
# image requires recover(), which is not yet supported on wasi
|
||||
# image fails on wasi, needs panic()/recover()
|
||||
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
# mime: fail on wasi; neds panic()/recover()
|
||||
# mime: fails on wasi, needs panic()/recover()
|
||||
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# mime/quotedprintable requires syscall.Faccessat
|
||||
# net/mail: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# net/ntextproto: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# regexp/syntax: fails on wasip1; needs panic()/recover()
|
||||
# strconv requires recover() which is not yet supported on wasi
|
||||
# text/tabwriter requires recover(), which is not yet supported on wasi
|
||||
# text/template/parse requires recover(), which is not yet supported on wasi
|
||||
# regexp/syntax: fails on wasip1, needs panic()/recover()
|
||||
# strconv: fails on wasi, needs panic()/recover()
|
||||
# text/tabwriter: fails on wasi, needs panic()/recover()
|
||||
# text/template/parse: fails on wasi, needs panic()/recover()
|
||||
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
|
||||
# Additional standard library packages that pass tests on individual platforms
|
||||
@@ -419,6 +425,7 @@ TEST_PACKAGES_LINUX := \
|
||||
os/user \
|
||||
regexp/syntax \
|
||||
strconv \
|
||||
testing/fstest \
|
||||
text/tabwriter \
|
||||
text/template/parse
|
||||
|
||||
@@ -429,7 +436,11 @@ TEST_PACKAGES_WINDOWS := \
|
||||
compress/flate \
|
||||
crypto/des \
|
||||
crypto/hmac \
|
||||
image \
|
||||
mime \
|
||||
regexp/syntax \
|
||||
strconv \
|
||||
text/tabwriter \
|
||||
text/template/parse \
|
||||
$(nil)
|
||||
|
||||
@@ -778,6 +789,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/blinky1
|
||||
@@ -947,6 +960,11 @@ ifneq ($(XTENSA), 0)
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32c6
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c6 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c6 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32s3
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
@@ -967,6 +985,8 @@ ifneq ($(XTENSA), 0)
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-box-3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
# esp32c3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@@ -1163,8 +1183,8 @@ endif
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/unistd build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/sources build/release/tinygo/lib/wasi-libc/libc-top-half
|
||||
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
|
||||
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp ${LLVM_PROJECTDIR}/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp ${LLVM_PROJECTDIR}/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp src build/release/tinygo/src
|
||||
@cp -rp targets build/release/tinygo/targets
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# TinyGo - Go compiler for small places
|
||||
|
||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml)
|
||||
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
||||
|
||||
|
||||
+29
-3
@@ -255,6 +255,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||
}
|
||||
|
||||
// Strip default initializers for -X globals from the type info before
|
||||
// building SSA. This prevents go/ssa from emitting init stores for them,
|
||||
// so that makeGlobalsModule can supply the correct values at final link
|
||||
// time without any runtime init overwriting them. The -X values themselves
|
||||
// are kept out of the per-package build cache; only the variable names
|
||||
// appear in the cache key.
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
for name := range globalValues[pkg.Pkg.Path()] {
|
||||
pkg.StripVarInitializer(name)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||
// program so it's pretty fast and doesn't need to be parallelized.
|
||||
program := lprogram.LoadSSA()
|
||||
@@ -477,7 +489,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if pkgInit.IsNil() {
|
||||
panic("init not found for " + pkg.Pkg.Path())
|
||||
}
|
||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1064,7 +1076,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp8266":
|
||||
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp32c6", "esp8266":
|
||||
// Special format for the ESP family of chips (parsed by the ROM
|
||||
// bootloader).
|
||||
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||
@@ -1196,7 +1208,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
||||
// needed to convert a program to its final form. Some transformations are not
|
||||
// optional and must be run as the compiler expects them to run.
|
||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
||||
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1340,6 +1352,14 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
}
|
||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
||||
|
||||
// Account for the bytes that tinygo_swapTask pushes onto the goroutine stack
|
||||
// on every context switch. The static analysis correctly traces Go calls,
|
||||
// but it cannot see into the assembly-level register push.
|
||||
var contextSwitchOverhead uint64
|
||||
if swapFuncs, ok := functions["tinygo_swapTask"]; ok && len(swapFuncs) == 1 {
|
||||
contextSwitchOverhead = swapFuncs[0].FrameSize
|
||||
}
|
||||
|
||||
sizes := make(map[string]functionStackSize)
|
||||
|
||||
// Add the reset handler function, for convenience. The reset handler runs
|
||||
@@ -1388,6 +1408,12 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
// overflow will occur even before the goroutine is started.
|
||||
stackSize = baseStackSize
|
||||
}
|
||||
if stackSizeType == stacksize.Bounded {
|
||||
// Add the overhead of context switching. This is needed because the
|
||||
// context switch (tinygo_swapTask) pushes callee-saved registers
|
||||
// onto the current stack, which is not seen by the static analysis.
|
||||
stackSize += contextSwitchOverhead
|
||||
}
|
||||
sizes[name] = functionStackSize{
|
||||
stackSize: stackSize,
|
||||
stackSizeType: stackSizeType,
|
||||
|
||||
@@ -28,6 +28,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32s3",
|
||||
"fe310",
|
||||
"gameboy-advance",
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
}
|
||||
|
||||
// Version range supported by TinyGo.
|
||||
const minorMin = 19
|
||||
const minorMin = 24 // when updating the min version, also update .github/workflows/compat.yml
|
||||
const minorMax = 26
|
||||
|
||||
// Check that we support this Go toolchain version.
|
||||
|
||||
+15
-3
@@ -100,12 +100,24 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
"esp32c6": 0x000d,
|
||||
"esp32s3": 0x0009,
|
||||
}[chip]
|
||||
|
||||
// SPI flash speed/size byte (byte 3 of header):
|
||||
// Upper nibble = flash size, lower nibble = flash frequency.
|
||||
// The espflasher auto-detects and patches the flash size (upper nibble),
|
||||
// but the frequency (lower nibble) must be correct per chip.
|
||||
spiSpeedSize := map[string]uint8{
|
||||
"esp32": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
"esp32c3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
"esp32c6": 0x10, // 80MHz=0x00, 2MB=0x10 (C6 uses different freq encoding)
|
||||
"esp32s3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
}[chip]
|
||||
|
||||
// Image header.
|
||||
switch chip {
|
||||
case "esp32", "esp32c3", "esp32s3":
|
||||
case "esp32", "esp32c3", "esp32s3", "esp32c6":
|
||||
// Header format:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||
// Note: not adding a SHA256 hash as the binary is modified by
|
||||
@@ -126,8 +138,8 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
}{
|
||||
magic: 0xE9,
|
||||
segment_count: byte(len(segments)),
|
||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||
spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB
|
||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||
spi_speed_size: spiSpeedSize,
|
||||
entry_addr: uint32(inf.Entry),
|
||||
wp_pin: 0xEE, // disable WP pin
|
||||
chip_id: chip_id,
|
||||
|
||||
+1
-1
@@ -218,7 +218,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
return err
|
||||
}
|
||||
// Store this archive in the cache.
|
||||
return os.Rename(f.Name(), archiveFilePath)
|
||||
return robustRename(f.Name(), archiveFilePath)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !windows
|
||||
|
||||
package builder
|
||||
|
||||
import "os"
|
||||
|
||||
func robustRename(oldpath, newpath string) error {
|
||||
return os.Rename(oldpath, newpath)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const robustRenameTimeout = 2 * time.Second
|
||||
|
||||
func robustRename(oldpath, newpath string) error {
|
||||
var bestErr error
|
||||
start := time.Now()
|
||||
nextSleep := time.Millisecond
|
||||
for {
|
||||
err := os.Rename(oldpath, newpath)
|
||||
if err == nil || !isEphemeralRenameError(err) {
|
||||
return err
|
||||
}
|
||||
if bestErr == nil {
|
||||
bestErr = err
|
||||
}
|
||||
if d := time.Since(start) + nextSleep; d >= robustRenameTimeout {
|
||||
return bestErr
|
||||
}
|
||||
time.Sleep(nextSleep)
|
||||
nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
|
||||
}
|
||||
}
|
||||
|
||||
func isEphemeralRenameError(err error) bool {
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) {
|
||||
switch errno {
|
||||
case syscall.Errno(2), // ERROR_FILE_NOT_FOUND
|
||||
syscall.Errno(5), // ERROR_ACCESS_DENIED
|
||||
syscall.Errno(32): // ERROR_SHARING_VIOLATION
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
+8
-2
@@ -501,8 +501,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
Align: section.Addralign,
|
||||
Type: memoryStack,
|
||||
})
|
||||
} else {
|
||||
// Regular .bss section.
|
||||
} else if section.Flags&elf.SHF_WRITE != 0 {
|
||||
// Regular .bss section. Zero-initialized RAM is always
|
||||
// writable, so require SHF_WRITE here.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
@@ -510,6 +511,11 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
Type: memoryBSS,
|
||||
})
|
||||
}
|
||||
// Other (non-writable) SHT_NOBITS sections are address-space
|
||||
// placeholders that occupy no RAM, such as the ESP linker
|
||||
// script's .irom_dummy / .rodata_dummy sections which reserve
|
||||
// the flash-mapped XIP virtual address ranges. They must not be
|
||||
// counted as bss/RAM usage.
|
||||
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_EXECINSTR != 0 {
|
||||
// .text
|
||||
sections = append(sections, memorySection{
|
||||
|
||||
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
|
||||
// This is a small number of very diverse targets that we want to test.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 3680, 280, 0, 2252},
|
||||
{"microbit", "examples/serial", 2694, 342, 8, 2248},
|
||||
{"wioterminal", "examples/pininterrupt", 7074, 1510, 120, 7248},
|
||||
{"hifive1b", "examples/echo", 3699, 297, 0, 2252},
|
||||
{"microbit", "examples/serial", 2736, 356, 8, 2248},
|
||||
{"wioterminal", "examples/pininterrupt", 7960, 1652, 132, 7480},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
@@ -99,7 +99,7 @@ func TestSizeFull(t *testing.T) {
|
||||
t.Fatal("could not read program size:", err)
|
||||
}
|
||||
for _, pkg := range sizes.sortedPackageNames() {
|
||||
if pkg == "(padding)" || pkg == "(unknown)" {
|
||||
if pkg == "(padding)" || pkg == "(unknown)" || pkg == "Go types" {
|
||||
// TODO: correctly attribute all unknown binary size.
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func RunTool(tool string, args ...string) error {
|
||||
var cflag *C.char
|
||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||
defer C.free(buf)
|
||||
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):len(args)]
|
||||
cflags := unsafe.Slice((**C.char)(buf), len(args))
|
||||
for i, flag := range args {
|
||||
cflag := C.CString(flag)
|
||||
cflags[i] = cflag
|
||||
|
||||
+8
-2
@@ -121,13 +121,19 @@ func parseLLDErrors(text string) error {
|
||||
if matches != nil {
|
||||
parsedError = true
|
||||
line, _ := strconv.Atoi(matches[3])
|
||||
// TODO: detect common mistakes like -gc=none?
|
||||
msg := "linker could not find symbol " + symbolName
|
||||
switch symbolName {
|
||||
case "runtime.alloc":
|
||||
msg = "object allocated on the heap with -gc=none"
|
||||
case "runtime.alloc_noheap":
|
||||
msg = "object allocated on the heap in //go:noheap function"
|
||||
}
|
||||
linkErrors = append(linkErrors, scanner.Error{
|
||||
Pos: token.Position{
|
||||
Filename: matches[2],
|
||||
Line: line,
|
||||
},
|
||||
Msg: "linker could not find symbol " + symbolName,
|
||||
Msg: msg,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
-22
@@ -26,10 +26,6 @@ import (
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Function that's only defined in Go 1.22.
|
||||
var setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
}
|
||||
|
||||
// cgoPackage holds all CGo-related information of a package.
|
||||
type cgoPackage struct {
|
||||
generated *ast.File
|
||||
@@ -654,7 +650,6 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "union",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: pos,
|
||||
@@ -708,7 +703,6 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -764,7 +758,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -811,11 +804,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
@@ -823,7 +811,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -881,7 +868,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -964,11 +950,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
@@ -976,7 +957,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -997,7 +977,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "value",
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
Type: bitfield.field.Type,
|
||||
@@ -1015,7 +994,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
//go:build go1.22
|
||||
|
||||
package cgo
|
||||
|
||||
// Code specifically for Go 1.22.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
f.FileStart = start
|
||||
f.FileEnd = end
|
||||
}
|
||||
}
|
||||
+5
-63
@@ -130,7 +130,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
// convert Go slice of strings to C array of strings.
|
||||
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
||||
defer C.free(cmdargsC)
|
||||
cmdargs := (*[1 << 16]*C.char)(cmdargsC)
|
||||
cmdargs := unsafe.Slice((**C.char)(cmdargsC), len(cflags))
|
||||
for i, cflag := range cflags {
|
||||
s := C.CString(cflag)
|
||||
cmdargs[i] = s
|
||||
@@ -190,7 +190,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
// Sanity check. This should (hopefully) never trigger.
|
||||
panic("libclang: file contents was not loaded")
|
||||
}
|
||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||
data := unsafe.Slice((*byte)(unsafe.Pointer(rawData)), size)
|
||||
|
||||
// Hash the contents if it isn't hashed yet.
|
||||
if _, ok := f.visitedFiles[path]; !ok {
|
||||
@@ -217,10 +217,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
exportName := name
|
||||
localName := name
|
||||
var stringSignature string
|
||||
@@ -258,7 +254,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + localName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: pos,
|
||||
@@ -295,11 +290,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: argName,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: argName,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: f.makeDecayingASTType(argType, pos),
|
||||
@@ -315,7 +305,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
}
|
||||
}
|
||||
obj.Decl = decl
|
||||
return decl, stringSignature
|
||||
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||
typ := f.makeASTRecordType(c, pos)
|
||||
@@ -325,39 +314,27 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
// Convert to a single-field struct type.
|
||||
typeExpr = f.makeUnionField(typ)
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typ.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, typ
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typeName := "_Cgo_" + name
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: f.makeASTType(underlyingType, pos),
|
||||
}
|
||||
if underlyingType.kind != C.CXType_Enum {
|
||||
typeSpec.Assign = pos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_VarDecl:
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
@@ -376,19 +353,13 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
},
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_MacroDefinition:
|
||||
@@ -405,26 +376,16 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_EnumDecl:
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||
// instead of types such as C.int, which are used here.
|
||||
@@ -432,12 +393,10 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Assign: pos,
|
||||
Type: f.makeASTType(underlying, pos),
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
@@ -452,19 +411,13 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
default:
|
||||
@@ -624,7 +577,7 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
||||
// now by reading the file from libclang.
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size]
|
||||
source := unsafe.Slice((*byte)(unsafe.Pointer(sourcePtr)), size)
|
||||
lines := []int{0}
|
||||
for i := 0; i < len(source)-1; i++ {
|
||||
if source[i] == '\n' {
|
||||
@@ -639,7 +592,8 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
||||
Package: f.Pos(0),
|
||||
Name: ast.NewIdent(p.packageName),
|
||||
}
|
||||
setASTFileFields(astFile, f.Pos(0), f.Pos(int(size)))
|
||||
astFile.FileStart = f.Pos(0)
|
||||
astFile.FileEnd = f.Pos(int(size))
|
||||
p.cgoFiles = append(p.cgoFiles, astFile)
|
||||
}
|
||||
positionFile := p.tokenFiles[filename]
|
||||
@@ -956,22 +910,16 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
|
||||
}
|
||||
|
||||
// Construct an *ast.TypeSpec for this type.
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: name,
|
||||
}
|
||||
spec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: goName,
|
||||
},
|
||||
}
|
||||
obj.Decl = spec
|
||||
return spec
|
||||
}
|
||||
|
||||
@@ -1104,7 +1052,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
pos: prevField.Names[0].NamePos,
|
||||
})
|
||||
prevField.Names[0].Name = bitfieldName
|
||||
prevField.Names[0].Obj.Name = bitfieldName
|
||||
}
|
||||
prevBitfield := &(*bitfieldList)[len(*bitfieldList)-1]
|
||||
prevBitfield.endBit = bitfieldOffset
|
||||
@@ -1121,11 +1068,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: name,
|
||||
Decl: field,
|
||||
},
|
||||
},
|
||||
}
|
||||
fieldList.List = append(fieldList.List, field)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include -I/usr/lib64/llvm19/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include -I/usr/lib64/llvm20/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
||||
|
||||
@@ -539,7 +539,7 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
||||
case "openocd", "msd", "command", "adb":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||
case "bmp":
|
||||
case "bmp", "probe-rs":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, ""
|
||||
default:
|
||||
|
||||
+41
-39
@@ -21,45 +21,47 @@ var (
|
||||
// usually passed from the command line, but can also be passed in environment
|
||||
// variables for example.
|
||||
type Options struct {
|
||||
GOOS string // environment variable
|
||||
GOARCH string // environment variable
|
||||
GOARM string // environment variable (only used with GOARCH=arm)
|
||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
||||
Directory string // working dir, leave it unset to use the current working dir
|
||||
Target string
|
||||
BuildMode string // -buildmode flag
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
||||
Serial string
|
||||
Work bool // -work flag to print temporary build directory
|
||||
InterpTimeout time.Duration
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
SkipDWARF bool
|
||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||
Debug bool
|
||||
Nobounds bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags []string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
ExtLDFlags []string
|
||||
GoCompatibility bool // enable to check for Go version compatibility
|
||||
GOOS string // environment variable
|
||||
GOARCH string // environment variable
|
||||
GOARM string // environment variable (only used with GOARCH=arm)
|
||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
||||
Directory string // working dir, leave it unset to use the current working dir
|
||||
Target string
|
||||
BuildMode string // -buildmode flag
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
||||
Serial string
|
||||
Work bool // -work flag to print temporary build directory
|
||||
InterpTimeout time.Duration
|
||||
InterpMaxLoopIterations int
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
SkipDWARF bool
|
||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||
Debug bool
|
||||
Nobounds bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintAllocsCover bool // emit allocs in go coverage tool format
|
||||
PrintStacks bool
|
||||
Tags []string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
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.
|
||||
|
||||
@@ -67,6 +67,7 @@ type TargetSpec struct {
|
||||
ADBPreCommands []string `json:"adb-pre-commands,omitempty"`
|
||||
ADBPushRemote string `json:"adb-push-remote,omitempty"`
|
||||
ADBPostCommands []string `json:"adb-post-commands,omitempty"`
|
||||
ProbeRSChip string `json:"probe-rs-chip,omitempty"`
|
||||
CodeModel string `json:"code-model,omitempty"`
|
||||
RelocationModel string `json:"relocation-model,omitempty"`
|
||||
WITPackage string `json:"wit-package,omitempty"`
|
||||
@@ -484,6 +485,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/runtime_windows.c")
|
||||
case "wasm", "wasip1", "wasip2":
|
||||
return nil, fmt.Errorf("GOOS=%s but GOARCH is unset. Please set GOARCH to wasm", options.GOOS)
|
||||
default:
|
||||
|
||||
+21
-8
@@ -241,24 +241,37 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
||||
}
|
||||
}
|
||||
|
||||
// Put the fault block at the end of the function and the next block at the
|
||||
// current insert position.
|
||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
faultBlock := b.getRuntimeAssertBlock(blockPrefix, assertFunc)
|
||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
|
||||
|
||||
// Now branch to the out-of-bounds or the regular block.
|
||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||
|
||||
// Fail: the assert triggered so panic.
|
||||
b.SetInsertPointAtEnd(faultBlock)
|
||||
b.createRuntimeCall(assertFunc, nil, "")
|
||||
b.CreateUnreachable()
|
||||
|
||||
// Ok: assert didn't trigger so continue normally.
|
||||
b.SetInsertPointAtEnd(nextBlock)
|
||||
}
|
||||
|
||||
func (b *builder) getRuntimeAssertBlock(blockPrefix, assertFunc string) llvm.BasicBlock {
|
||||
if b.runtimeAssertBlocks == nil {
|
||||
b.runtimeAssertBlocks = make(map[string]llvm.BasicBlock)
|
||||
}
|
||||
if block := b.runtimeAssertBlocks[assertFunc]; !block.IsNil() {
|
||||
return block
|
||||
}
|
||||
savedBlock := b.GetInsertBlock()
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
b.runtimeAssertBlocks[assertFunc] = block
|
||||
b.SetInsertPointAtEnd(block)
|
||||
if b.hasDeferFrame() {
|
||||
b.createFaultCheckpoint()
|
||||
}
|
||||
b.createRuntimeCall(assertFunc, nil, "")
|
||||
b.CreateUnreachable()
|
||||
b.SetInsertPointAtEnd(savedBlock)
|
||||
return block
|
||||
}
|
||||
|
||||
// extendInteger extends the value to at least targetType using a zero or sign
|
||||
// extend. The resulting value is not truncated: it may still be bigger than
|
||||
// targetType.
|
||||
|
||||
+2
-2
@@ -48,7 +48,7 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||
|
||||
// Do the send.
|
||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
||||
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
||||
|
||||
// End the lifetime of the allocas.
|
||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||
@@ -101,7 +101,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
|
||||
// createChanClose closes the given channel.
|
||||
func (b *builder) createChanClose(ch llvm.Value) {
|
||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
||||
b.createRuntimeInvoke("chanClose", []llvm.Value{ch}, "")
|
||||
}
|
||||
|
||||
// createSelect emits all IR necessary for a select statements. That's a
|
||||
|
||||
+27
-8
@@ -92,6 +92,7 @@ type compilerContext struct {
|
||||
pkg *types.Package
|
||||
packageDir string // directory for this package
|
||||
runtimePkg *types.Package
|
||||
localTypeNames typeutil.Map // *types.Named (synthetic local from generic instantiation) -> string
|
||||
}
|
||||
|
||||
// newCompilerContext returns a new compiler context ready for use, most
|
||||
@@ -176,6 +177,9 @@ type builder struct {
|
||||
deferBuiltinFuncs map[ssa.Value]deferBuiltin
|
||||
runDefersBlock []llvm.BasicBlock
|
||||
afterDefersBlock []llvm.BasicBlock
|
||||
|
||||
runtimeAssertBlocks map[string]llvm.BasicBlock
|
||||
interfaceAssertBlock llvm.BasicBlock
|
||||
}
|
||||
|
||||
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
|
||||
@@ -300,6 +304,11 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
// Convert AST to SSA.
|
||||
ssaPkg.Build()
|
||||
|
||||
// Assign names to function-local named types before compiling the
|
||||
// package, so that types declared in different functions (or in
|
||||
// different instantiations of a generic function) do not collide.
|
||||
c.scanLocalTypes(ssaPkg)
|
||||
|
||||
// Initialize debug information.
|
||||
if c.Debug {
|
||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
@@ -314,9 +323,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
// Load comments such as //go:extern on globals.
|
||||
c.loadASTComments(pkg)
|
||||
|
||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
||||
// functionality.
|
||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
||||
if c.NeedsStackObjects {
|
||||
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
|
||||
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
|
||||
@@ -862,10 +868,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
|
||||
if ok := b.defineMathOp(); ok {
|
||||
// The body of this function (if there is one) is ignored and
|
||||
// replaced with a LLVM intrinsic call.
|
||||
b.defineMathOp()
|
||||
continue
|
||||
}
|
||||
if ok := b.defineMathBitsIntrinsic(); ok {
|
||||
@@ -1349,6 +1354,15 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
||||
// diagnostic.
|
||||
func (b *builder) createFunction() {
|
||||
b.createFunctionStart(false)
|
||||
// createFunctionStart returns early (leaving blockInfo unset) when it hits
|
||||
// an error such as a redeclared symbol. Don't plow into the block loop and
|
||||
// panic on an empty blockInfo — the error was already recorded.
|
||||
if b.blockInfo == nil {
|
||||
// TINYGO-DEBUG: surface the redeclaration collision.
|
||||
fmt.Printf("TINYGO-DEBUG skipping fn (redeclared/errored): %s pkg=%s\n",
|
||||
b.fn.RelString(nil), b.fn.Pkg.Pkg.Path())
|
||||
return
|
||||
}
|
||||
|
||||
// Fill blocks with instructions.
|
||||
for _, block := range b.fn.DomPreorder() {
|
||||
@@ -1888,6 +1902,12 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
// not of the current function.
|
||||
useParentFrame = 1
|
||||
}
|
||||
// Prevent inlining of functions that call recover(), matching the
|
||||
// Go compiler's behavior. If this function were inlined into a
|
||||
// deferred function, recover() would incorrectly succeed because
|
||||
// the inlined code runs in the deferred function's context.
|
||||
noinline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0)
|
||||
b.llvmFn.AddFunctionAttr(noinline)
|
||||
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
|
||||
case "ssa:wrapnilchk":
|
||||
// TODO: do an actual nil check?
|
||||
@@ -2176,9 +2196,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
}
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
||||
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
||||
align := b.targetData.ABITypeAlignment(typ)
|
||||
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
buf := b.createAlloc(sizeValue, layoutValue, align, expr.Comment)
|
||||
return buf, nil
|
||||
} else {
|
||||
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
|
||||
@@ -2408,7 +2427,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
}
|
||||
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
||||
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
||||
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
||||
slicePtr := b.createAlloc(sliceSize, layoutValue, 0, "makeslice.buf")
|
||||
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
|
||||
|
||||
// Extend or truncate if necessary. This is safe as we've already done
|
||||
|
||||
@@ -50,6 +50,7 @@ func TestCompiler(t *testing.T) {
|
||||
{"channel.go", "", ""},
|
||||
{"gc.go", "", ""},
|
||||
{"zeromap.go", "", ""},
|
||||
{"generics.go", "", ""},
|
||||
}
|
||||
if goMinor >= 20 {
|
||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||
|
||||
+22
-5
@@ -60,10 +60,6 @@ func (b *builder) deferInitFunc() {
|
||||
b.deferExprFuncs = make(map[ssa.Value]int)
|
||||
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||
|
||||
// Create defer list pointer.
|
||||
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
||||
|
||||
if b.hasDeferFrame() {
|
||||
// Set up the defer frame with the current stack pointer.
|
||||
// This assumes that the stack pointer doesn't move outside of the
|
||||
@@ -73,12 +69,22 @@ func (b *builder) deferInitFunc() {
|
||||
// in the setjmp-like inline assembly.
|
||||
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||
// The field index must match the DeferPtr field in runtime.deferFrame,
|
||||
// defined in src/runtime/panic.go.
|
||||
b.deferPtr = b.CreateInBoundsGEP(deferFrameType, b.deferFrame, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 6, false), // DeferPtr field
|
||||
}, "deferPtr")
|
||||
stackPointer := b.readStackPointer()
|
||||
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
|
||||
|
||||
// Create the landing pad block, which is where control transfers after
|
||||
// a panic.
|
||||
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
|
||||
} else {
|
||||
// Create defer list pointer.
|
||||
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +243,17 @@ func (b *builder) createInvokeCheckpoint() {
|
||||
b.currentBlockInfo.exit = continueBB
|
||||
}
|
||||
|
||||
// createFaultCheckpoint is like createInvokeCheckpoint but for use in fault
|
||||
// blocks (e.g., bounds check failures). Unlike createInvokeCheckpoint, it does
|
||||
// not update currentBlockInfo.exit because the fault block is a dead-end that
|
||||
// does not participate in phi node resolution.
|
||||
func (b *builder) createFaultCheckpoint() {
|
||||
isZero := b.createCheckpoint(b.deferFrame)
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from the current block to itself.
|
||||
// Use Tarjan's strongly connected components algorithm to search for cycles.
|
||||
// A one-node SCC is a cycle iff there is an edge from the node to itself.
|
||||
@@ -488,7 +505,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
nilPtr := llvm.ConstNull(b.dataPtrType)
|
||||
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||
alloca = b.createAlloc(sizeValue, nilPtr, 0, "defer.alloc.call")
|
||||
}
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(alloca)
|
||||
|
||||
@@ -10,6 +10,32 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Heap-allocate a buffer of the given size. This will typically call
|
||||
// runtime.alloc.
|
||||
func (b *builder) createAlloc(sizeValue, layoutValue llvm.Value, align int, comment string) llvm.Value {
|
||||
// Normally allocate using "runtime.alloc", but use "runtime.alloc_noheap"
|
||||
// if the //go:noheap pragma is used.
|
||||
allocFunc := "alloc"
|
||||
if b.info.noheap {
|
||||
allocFunc = "alloc_noheap"
|
||||
}
|
||||
|
||||
// Allocs that don't allocate anything can return an architecture-specific
|
||||
// sentinel value.
|
||||
if !sizeValue.IsAConstantInt().IsNil() && sizeValue.ZExtValue() == 0 {
|
||||
allocFunc = "alloc_zero"
|
||||
}
|
||||
|
||||
// Make the runtime call.
|
||||
call := b.createRuntimeCall(allocFunc, []llvm.Value{sizeValue, layoutValue}, comment)
|
||||
if align != 0 {
|
||||
// TODO: make sure all callsites set the correct alignment.
|
||||
call.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
}
|
||||
|
||||
return call
|
||||
}
|
||||
|
||||
// trackExpr inserts pointer tracking intrinsics for the GC if the expression is
|
||||
// one of the expressions that need this.
|
||||
func (b *builder) trackExpr(expr ssa.Value, value llvm.Value) {
|
||||
|
||||
+328
-46
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -165,7 +166,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
}
|
||||
}
|
||||
|
||||
typeCodeName, isLocal := getTypeCodeName(typ)
|
||||
typeCodeName, isLocal := c.getTypeCodeName(typ)
|
||||
globalName := "reflect/types.type:" + typeCodeName
|
||||
var global llvm.Value
|
||||
if isLocal {
|
||||
@@ -580,20 +581,50 @@ var basicTypeNames = [...]string{
|
||||
// getTypeCodeName returns a name for this type that can be used in the
|
||||
// interface lowering pass to assign type codes as expected by the reflect
|
||||
// package. See getTypeCodeNum.
|
||||
func getTypeCodeName(t types.Type) (string, bool) {
|
||||
//
|
||||
// isLocal is true when the type is declared inside a function body.
|
||||
// Such types need a per-declaration (or per instantiation) suffix
|
||||
// because their printed names are not unique.
|
||||
//
|
||||
// Ordinary function-local types (TypeName.Parent() != nil) are
|
||||
// disambiguated lazily from their declaration position: every
|
||||
// declaration in the package has a distinct (file, line, column)
|
||||
// triple, and the position is taken un-//line-adjusted so it is
|
||||
// stable across builds. Such types are only nameable inside their
|
||||
// declaring package, so the name does not need to agree with anything
|
||||
// computed in another package.
|
||||
//
|
||||
// Synthetic locals (TypeName.Parent() == nil), produced by generic
|
||||
// instantiation, are pre-registered by scanLocalTypes because their
|
||||
// names must agree across packages that materialize the same instance.
|
||||
func (c *compilerContext) getTypeCodeName(t types.Type) (name string, isLocal bool) {
|
||||
switch t := types.Unalias(t).(type) {
|
||||
case *types.Named:
|
||||
if t.Obj().Parent() != t.Obj().Pkg().Scope() {
|
||||
return "named:" + t.String() + "$local", true
|
||||
tn := t.Obj()
|
||||
if tn.Pkg() == nil || tn.Parent() == tn.Pkg().Scope() {
|
||||
// Package-scope or builtin: the printed name is unique.
|
||||
return "named:" + t.String(), false
|
||||
}
|
||||
return "named:" + t.String(), false
|
||||
if tn.Parent() != nil {
|
||||
// Ordinary function-local type. Use the un-//line-adjusted
|
||||
// declaration position as the disambiguator.
|
||||
pos := c.program.Fset.PositionFor(tn.Pos(), false)
|
||||
return fmt.Sprintf("named:%s$%s:%d:%d", t.String(), filepath.Base(pos.Filename), pos.Line, pos.Column), true
|
||||
}
|
||||
// Synthetic local from generic instantiation: must have been
|
||||
// pre-registered by scanLocalTypes.
|
||||
v := c.localTypeNames.At(t)
|
||||
if v == nil {
|
||||
panic("compiler: synthetic local type " + tn.Name() + " was not registered by scanLocalTypes")
|
||||
}
|
||||
return "named:" + v.(string), true
|
||||
case *types.Array:
|
||||
s, isLocal := getTypeCodeName(t.Elem())
|
||||
s, isLocal := c.getTypeCodeName(t.Elem())
|
||||
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + s, isLocal
|
||||
case *types.Basic:
|
||||
return "basic:" + basicTypeNames[t.Kind()], false
|
||||
case *types.Chan:
|
||||
s, isLocal := getTypeCodeName(t.Elem())
|
||||
s, isLocal := c.getTypeCodeName(t.Elem())
|
||||
var dir string
|
||||
switch t.Dir() {
|
||||
case types.SendOnly:
|
||||
@@ -613,7 +644,7 @@ func getTypeCodeName(t types.Type) (string, bool) {
|
||||
if !token.IsExported(name) {
|
||||
name = t.Method(i).Pkg().Path() + "." + name
|
||||
}
|
||||
s, local := getTypeCodeName(t.Method(i).Type())
|
||||
s, local := c.getTypeCodeName(t.Method(i).Type())
|
||||
if local {
|
||||
isLocal = true
|
||||
}
|
||||
@@ -621,17 +652,17 @@ func getTypeCodeName(t types.Type) (string, bool) {
|
||||
}
|
||||
return "interface:" + "{" + strings.Join(methods, ",") + "}", isLocal
|
||||
case *types.Map:
|
||||
keyType, keyLocal := getTypeCodeName(t.Key())
|
||||
elemType, elemLocal := getTypeCodeName(t.Elem())
|
||||
keyType, keyLocal := c.getTypeCodeName(t.Key())
|
||||
elemType, elemLocal := c.getTypeCodeName(t.Elem())
|
||||
return "map:" + "{" + keyType + "," + elemType + "}", keyLocal || elemLocal
|
||||
case *types.Pointer:
|
||||
s, isLocal := getTypeCodeName(t.Elem())
|
||||
s, isLocal := c.getTypeCodeName(t.Elem())
|
||||
return "pointer:" + s, isLocal
|
||||
case *types.Signature:
|
||||
isLocal := false
|
||||
params := make([]string, t.Params().Len())
|
||||
for i := 0; i < t.Params().Len(); i++ {
|
||||
s, local := getTypeCodeName(t.Params().At(i).Type())
|
||||
s, local := c.getTypeCodeName(t.Params().At(i).Type())
|
||||
if local {
|
||||
isLocal = true
|
||||
}
|
||||
@@ -639,7 +670,7 @@ func getTypeCodeName(t types.Type) (string, bool) {
|
||||
}
|
||||
results := make([]string, t.Results().Len())
|
||||
for i := 0; i < t.Results().Len(); i++ {
|
||||
s, local := getTypeCodeName(t.Results().At(i).Type())
|
||||
s, local := c.getTypeCodeName(t.Results().At(i).Type())
|
||||
if local {
|
||||
isLocal = true
|
||||
}
|
||||
@@ -647,7 +678,7 @@ func getTypeCodeName(t types.Type) (string, bool) {
|
||||
}
|
||||
return "func:" + "{" + strings.Join(params, ",") + "}{" + strings.Join(results, ",") + "}", isLocal
|
||||
case *types.Slice:
|
||||
s, isLocal := getTypeCodeName(t.Elem())
|
||||
s, isLocal := c.getTypeCodeName(t.Elem())
|
||||
return "slice:" + s, isLocal
|
||||
case *types.Struct:
|
||||
elems := make([]string, t.NumFields())
|
||||
@@ -657,7 +688,7 @@ func getTypeCodeName(t types.Type) (string, bool) {
|
||||
if t.Field(i).Embedded() {
|
||||
embedded = "#"
|
||||
}
|
||||
s, local := getTypeCodeName(t.Field(i).Type())
|
||||
s, local := c.getTypeCodeName(t.Field(i).Type())
|
||||
if local {
|
||||
isLocal = true
|
||||
}
|
||||
@@ -672,6 +703,232 @@ func getTypeCodeName(t types.Type) (string, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// scanLocalTypes assigns names to every synthetic *types.TypeName
|
||||
// (TypeName.Parent() == nil) reachable from this package and stores
|
||||
// them in c.localTypeNames.
|
||||
//
|
||||
// Synthetic TypeNames are produced by generic instantiation: two
|
||||
// instantiations of the same generic function (e.g. F[int] and
|
||||
// F[string]) produce TypeNames with the same printed name and the
|
||||
// same source position, so each is named with the enclosing
|
||||
// instance's RelString as prefix. RelString encodes the type
|
||||
// arguments, matching Go's runtime behavior, where F[int].Inner and
|
||||
// F[string].Inner are distinct types even when Inner does not mention
|
||||
// the type parameter.
|
||||
//
|
||||
// A given instance may be materialized by several packages (the body
|
||||
// of F[int] is compiled in every package that calls F[int]); its
|
||||
// reflect/types.type:* global has LinkOnceODRLinkage and is merged by
|
||||
// name at link time. The chosen name therefore depends only on
|
||||
// intrinsic SSA properties (RelString and the raw token.Pos used as a
|
||||
// sort key), so any package compiling the same instance produces the
|
||||
// same identifier.
|
||||
//
|
||||
// Ordinary function-local TypeNames (TypeName.Parent() != nil) are
|
||||
// not handled here: they are nameable only inside their declaring
|
||||
// package, and getTypeCodeName derives a stable per-declaration name
|
||||
// for them directly from their source position.
|
||||
func (c *compilerContext) scanLocalTypes(ssaPkg *ssa.Package) {
|
||||
// Locate every generic instance reachable from this package
|
||||
// (including instances declared in imported packages and any
|
||||
// function reached through an instance subtree).
|
||||
var instances []*ssa.Function
|
||||
seen := map[*ssa.Function]struct{}{}
|
||||
var walk func(fn *ssa.Function, inInstance bool)
|
||||
walk = func(fn *ssa.Function, inInstance bool) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
if _, ok := seen[fn]; ok {
|
||||
return
|
||||
}
|
||||
// fn belongs to an instance subtree if it is itself an
|
||||
// instantiation or if we reached it from one.
|
||||
//
|
||||
// len(TypeArgs()) is used instead of fn.Origin() because
|
||||
// Origin() may call Build() on fn's declaring package, which
|
||||
// would defeat per-package compilation.
|
||||
isInstanceRoot := len(fn.TypeArgs()) > 0
|
||||
if !isInstanceRoot && !inInstance && fn.Pkg != ssaPkg {
|
||||
return
|
||||
}
|
||||
if fn.Blocks == nil && fn.AnonFuncs == nil {
|
||||
return
|
||||
}
|
||||
seen[fn] = struct{}{}
|
||||
isInInstance := inInstance || isInstanceRoot
|
||||
if isInInstance {
|
||||
instances = append(instances, fn)
|
||||
}
|
||||
for _, anon := range fn.AnonFuncs {
|
||||
walk(anon, isInInstance)
|
||||
}
|
||||
var ops [10]*ssa.Value
|
||||
for _, b := range fn.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
for _, op := range instr.Operands(ops[:0]) {
|
||||
if op == nil || *op == nil {
|
||||
continue
|
||||
}
|
||||
if callee, ok := (*op).(*ssa.Function); ok {
|
||||
walk(callee, isInInstance)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, member := range ssaPkg.Members {
|
||||
switch m := member.(type) {
|
||||
case *ssa.Function:
|
||||
walk(m, false)
|
||||
case *ssa.Type:
|
||||
mset := c.program.MethodSets.MethodSet(m.Type())
|
||||
for i := 0; i < mset.Len(); i++ {
|
||||
walk(c.program.MethodValue(mset.At(i)), false)
|
||||
}
|
||||
pmset := c.program.MethodSets.MethodSet(types.NewPointer(m.Type()))
|
||||
for i := 0; i < pmset.Len(); i++ {
|
||||
walk(c.program.MethodValue(pmset.At(i)), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Registration is first-writer-wins (a synthetic TypeName may be
|
||||
// reachable from several instances), so visit instances in a
|
||||
// deterministic order. Pos() is a defensive tiebreaker.
|
||||
sort.Slice(instances, func(i, j int) bool {
|
||||
ri, rj := instances[i].RelString(nil), instances[j].RelString(nil)
|
||||
if ri != rj {
|
||||
return ri < rj
|
||||
}
|
||||
return instances[i].Pos() < instances[j].Pos()
|
||||
})
|
||||
for _, fn := range instances {
|
||||
c.registerSyntheticLocalTypes(fn)
|
||||
}
|
||||
}
|
||||
|
||||
// registerSyntheticLocalTypes walks every type reachable from fn's
|
||||
// body and records each synthetic *types.Named (TypeName.Parent() ==
|
||||
// nil) in c.localTypeNames. Each is named with fn.RelString as the
|
||||
// owning function plus a per-function counter assigned in source
|
||||
// order.
|
||||
//
|
||||
// First-writer-wins: a *types.Named already present in
|
||||
// c.localTypeNames is left alone, so a synthetic type reachable from
|
||||
// several instances keeps the name assigned by the first (in
|
||||
// scanLocalTypes' deterministic order).
|
||||
func (c *compilerContext) registerSyntheticLocalTypes(fn *ssa.Function) {
|
||||
var found []*types.Named
|
||||
seen := map[types.Type]struct{}{}
|
||||
var visit func(t types.Type)
|
||||
visit = func(t types.Type) {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
if _, ok := seen[t]; ok {
|
||||
return
|
||||
}
|
||||
seen[t] = struct{}{}
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
visit(types.Unalias(t))
|
||||
case *types.Named:
|
||||
tn := t.Obj()
|
||||
if tn.Pkg() != nil && tn.Parent() == nil {
|
||||
if c.localTypeNames.At(t) == nil {
|
||||
// Reserve the slot so later calls within this
|
||||
// scanLocalTypes invocation skip it; the final
|
||||
// name is filled in after sorting, before any
|
||||
// getTypeCodeName lookups happen.
|
||||
c.localTypeNames.Set(t, "")
|
||||
found = append(found, t)
|
||||
}
|
||||
}
|
||||
targs := t.TypeArgs()
|
||||
for i := 0; i < targs.Len(); i++ {
|
||||
visit(targs.At(i))
|
||||
}
|
||||
visit(t.Underlying())
|
||||
case *types.Pointer:
|
||||
visit(t.Elem())
|
||||
case *types.Slice:
|
||||
visit(t.Elem())
|
||||
case *types.Array:
|
||||
visit(t.Elem())
|
||||
case *types.Chan:
|
||||
visit(t.Elem())
|
||||
case *types.Map:
|
||||
visit(t.Key())
|
||||
visit(t.Elem())
|
||||
case *types.Struct:
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
visit(t.Field(i).Type())
|
||||
}
|
||||
case *types.Signature:
|
||||
if p := t.Params(); p != nil {
|
||||
for i := 0; i < p.Len(); i++ {
|
||||
visit(p.At(i).Type())
|
||||
}
|
||||
}
|
||||
if r := t.Results(); r != nil {
|
||||
for i := 0; i < r.Len(); i++ {
|
||||
visit(r.At(i).Type())
|
||||
}
|
||||
}
|
||||
case *types.Tuple:
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
visit(t.At(i).Type())
|
||||
}
|
||||
case *types.Interface:
|
||||
// A synthetic local type can be reachable only through a
|
||||
// local interface's method signature, so descend into
|
||||
// them. getTypeCodeName encodes those signatures into
|
||||
// the interface's identifier, and the seen map breaks
|
||||
// cycles formed by methods that mention the interface
|
||||
// itself.
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
visit(t.Method(i).Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, p := range fn.Params {
|
||||
visit(p.Type())
|
||||
}
|
||||
for _, fv := range fn.FreeVars {
|
||||
visit(fv.Type())
|
||||
}
|
||||
for _, l := range fn.Locals {
|
||||
visit(l.Type())
|
||||
}
|
||||
var ops [10]*ssa.Value
|
||||
for _, b := range fn.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
if v, ok := instr.(ssa.Value); ok {
|
||||
visit(v.Type())
|
||||
}
|
||||
for _, op := range instr.Operands(ops[:0]) {
|
||||
if op != nil && *op != nil {
|
||||
visit((*op).Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(found) == 0 {
|
||||
return
|
||||
}
|
||||
// Sort by raw token.Pos: this gives a total order on declarations
|
||||
// that is stable across builds and unaffected by //line directives
|
||||
// (which only adjust the human-facing position from Fset.Position).
|
||||
sort.Slice(found, func(i, j int) bool {
|
||||
return found[i].Obj().Pos() < found[j].Obj().Pos()
|
||||
})
|
||||
enclosing := fn.RelString(nil)
|
||||
for i, named := range found {
|
||||
c.localTypeNames.Set(named, fmt.Sprintf("%s.%s$%d", enclosing, named.Obj().Name(), i))
|
||||
}
|
||||
}
|
||||
|
||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||
// method set should be unreferenced after the interface lowering pass.
|
||||
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
@@ -769,7 +1026,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
commaOk = b.createInterfaceTypeAssert(intf, actualTypeNum)
|
||||
}
|
||||
} else {
|
||||
name, _ := getTypeCodeName(expr.AssertedType)
|
||||
name, _ := b.getTypeCodeName(expr.AssertedType)
|
||||
globalName := "reflect/types.typeid:" + name
|
||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||
if assertedTypeCodeGlobal.IsNil() {
|
||||
@@ -796,43 +1053,68 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
|
||||
prevBlock := b.GetInsertBlock()
|
||||
okBlock := b.insertBasicBlock("typeassert.ok")
|
||||
nextBlock := b.insertBasicBlock("typeassert.next")
|
||||
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
|
||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
|
||||
// Retrieve the value from the interface if the type assert was
|
||||
// successful.
|
||||
b.SetInsertPointAtEnd(okBlock)
|
||||
var valueOk llvm.Value
|
||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
// Type assert on interface type. Easy: just return the same
|
||||
// interface value.
|
||||
valueOk = itf
|
||||
} else {
|
||||
// Type assert on concrete type. Extract the underlying type from
|
||||
// the interface (but only after checking it matches).
|
||||
valueOk = b.extractValueFromInterface(itf, assertedType)
|
||||
}
|
||||
b.CreateBr(nextBlock)
|
||||
|
||||
// Continue after the if statement.
|
||||
b.SetInsertPointAtEnd(nextBlock)
|
||||
phi := b.CreatePHI(assertedType, "typeassert.value")
|
||||
phi.AddIncoming([]llvm.Value{llvm.ConstNull(assertedType), valueOk}, []llvm.BasicBlock{prevBlock, okBlock})
|
||||
|
||||
if expr.CommaOk {
|
||||
nextBlock := b.insertBasicBlock("typeassert.next")
|
||||
b.currentBlockInfo.exit = nextBlock
|
||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
|
||||
// Retrieve the value from the interface if the type assert was
|
||||
// successful.
|
||||
b.SetInsertPointAtEnd(okBlock)
|
||||
var valueOk llvm.Value
|
||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
// Type assert on interface type. Easy: just return the same
|
||||
// interface value.
|
||||
valueOk = itf
|
||||
} else {
|
||||
// Type assert on concrete type. Extract the underlying type from
|
||||
// the interface (but only after checking it matches).
|
||||
valueOk = b.extractValueFromInterface(itf, assertedType)
|
||||
}
|
||||
b.CreateBr(nextBlock)
|
||||
|
||||
// Continue after the if statement.
|
||||
b.SetInsertPointAtEnd(nextBlock)
|
||||
phi := b.CreatePHI(assertedType, "typeassert.value")
|
||||
phi.AddIncoming([]llvm.Value{llvm.ConstNull(assertedType), valueOk}, []llvm.BasicBlock{prevBlock, okBlock})
|
||||
|
||||
tuple := b.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(b.ctx.Int1Type())}, false) // create empty tuple
|
||||
tuple = b.CreateInsertValue(tuple, phi, 0, "") // insert value
|
||||
tuple = b.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean
|
||||
return tuple
|
||||
} else {
|
||||
// This is kind of dirty as the branch above becomes mostly useless,
|
||||
// but hopefully this gets optimized away.
|
||||
b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
|
||||
return phi
|
||||
// Type assert without comma-ok. If it fails, panic.
|
||||
faultBlock := b.getInterfaceAssertBlock()
|
||||
b.currentBlockInfo.exit = okBlock
|
||||
b.CreateCondBr(commaOk, okBlock, faultBlock)
|
||||
|
||||
// OK: extract the value from the interface.
|
||||
b.SetInsertPointAtEnd(okBlock)
|
||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
return itf
|
||||
}
|
||||
return b.extractValueFromInterface(itf, assertedType)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) getInterfaceAssertBlock() llvm.BasicBlock {
|
||||
if !b.interfaceAssertBlock.IsNil() {
|
||||
return b.interfaceAssertBlock
|
||||
}
|
||||
savedBlock := b.GetInsertBlock()
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.throw")
|
||||
b.interfaceAssertBlock = block
|
||||
b.SetInsertPointAtEnd(block)
|
||||
if b.hasDeferFrame() {
|
||||
b.createFaultCheckpoint()
|
||||
}
|
||||
b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "")
|
||||
b.CreateUnreachable()
|
||||
b.SetInsertPointAtEnd(savedBlock)
|
||||
return block
|
||||
}
|
||||
|
||||
// getMethodsString returns a string to be used in the "tinygo-methods" string
|
||||
// attribute for interface functions.
|
||||
func (c *compilerContext) getMethodsString(itf *types.Interface) string {
|
||||
@@ -857,7 +1139,7 @@ func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
|
||||
if !token.IsExported(name) {
|
||||
name = method.Pkg().Path() + "." + name
|
||||
}
|
||||
s, _ := getTypeCodeName(method.Type())
|
||||
s, _ := c.getTypeCodeName(method.Type())
|
||||
globalName := "reflect/types.signature:" + name + ":" + s
|
||||
value := c.mod.NamedGlobal(globalName)
|
||||
if value.IsNil() {
|
||||
@@ -901,7 +1183,7 @@ func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
|
||||
// thunk is declared, not defined: it will be defined by the interface lowering
|
||||
// pass.
|
||||
func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
||||
s, _ := getTypeCodeName(instr.Value.Type().Underlying())
|
||||
s, _ := c.getTypeCodeName(instr.Value.Type().Underlying())
|
||||
fnName := s + "." + instr.Method.Name() + "$invoke"
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
@@ -926,7 +1208,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
||||
// by the interface lowering pass as a type-ID comparison chain, avoiding the
|
||||
// need for runtime.typeImplementsMethodSet at compile time.
|
||||
func (b *builder) createInterfaceTypeAssert(intf *types.Interface, actualType llvm.Value) llvm.Value {
|
||||
s, _ := getTypeCodeName(intf)
|
||||
s, _ := b.getTypeCodeName(intf)
|
||||
fnName := s + ".$typeassert"
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
|
||||
+42
-8
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -166,12 +167,22 @@ func (b *builder) createMachineKeepAliveImpl() {
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
"math.Acos": "llvm.acos.f64",
|
||||
"math.Asin": "llvm.asin.f64",
|
||||
"math.Atan": "llvm.atan.f64",
|
||||
"math.Atan2": "llvm.atan2.f64",
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Cos": "llvm.cos.f64",
|
||||
"math.Cosh": "llvm.cosh.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
"math.Exp2": "llvm.exp2.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Log": "llvm.log.f64",
|
||||
"math.Sin": "llvm.sin.f64",
|
||||
"math.Sinh": "llvm.sinh.f64",
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Tan": "llvm.tan.f64",
|
||||
"math.Tanh": "llvm.tanh.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
@@ -185,18 +196,40 @@ var mathToLLVMMapping = map[string]string{
|
||||
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
||||
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||
// more expensive than 32-bit ones.
|
||||
func (b *builder) defineMathOp() {
|
||||
b.createFunctionStart(true)
|
||||
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||
if llvmName == "" {
|
||||
panic("unreachable: unknown math operation") // sanity check
|
||||
func (b *builder) defineMathOp() bool {
|
||||
llvmName, ok := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(b.Triple, "-wasi") || llvmutil.Version() < 19 {
|
||||
// We don't have a real libc for wasip2. Until that is fixed, we need to
|
||||
// limit math intrinsics on WASI to a subset supported natively in
|
||||
// WebAssembly.
|
||||
// Also, since we don't know the specific libc we will target, disallow
|
||||
// these for all WASI targets.
|
||||
//
|
||||
// We also need to limit ourselves to LLVM 19 and above for the extended
|
||||
// set of math intrinsics, see:
|
||||
// https://discourse.llvm.org/t/rfc-all-the-math-intrinsics/78294
|
||||
switch b.fn.Name() {
|
||||
case "Ceil", "Exp", "Exp2", "Floor", "Log", "Sqrt", "Trunc":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
b.createFunctionStart(true)
|
||||
llvmFn := b.mod.NamedFunction(llvmName)
|
||||
if llvmFn.IsNil() {
|
||||
// The intrinsic doesn't exist yet, so declare it.
|
||||
// At the moment, all supported intrinsics have the form "double
|
||||
// foo(double %x)" so we can hardcode the signature here.
|
||||
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
var llvmType llvm.Type
|
||||
switch b.fn.Name() {
|
||||
case "Atan2":
|
||||
// double atan2(double %y, double %x)
|
||||
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)
|
||||
default:
|
||||
// double foo(double %x)
|
||||
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
}
|
||||
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
|
||||
}
|
||||
// Create a call to the intrinsic.
|
||||
@@ -206,6 +239,7 @@ func (b *builder) defineMathOp() {
|
||||
}
|
||||
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *builder) defineCryptoIntrinsic() bool {
|
||||
|
||||
+1
-7
@@ -129,13 +129,7 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
align := b.targetData.ABITypeAlignment(packedType)
|
||||
alloc := b.mod.NamedFunction("runtime.alloc")
|
||||
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
||||
sizeValue,
|
||||
llvm.ConstNull(b.dataPtrType),
|
||||
llvm.Undef(b.dataPtrType), // unused context parameter
|
||||
}, "")
|
||||
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
packedAlloc := b.createAlloc(sizeValue, llvm.ConstNull(b.dataPtrType), align, "")
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(packedAlloc)
|
||||
}
|
||||
|
||||
+455
-168
@@ -3,42 +3,28 @@ package compiler
|
||||
// This file emits the correct map intrinsics for map operations.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/tinygo-org/tinygo/src/tinygo"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
const hashArrayUnrollLimit = 4
|
||||
|
||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||
// initializing an appropriately sized object.
|
||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
mapType := expr.Type().Underlying().(*types.Map)
|
||||
keyType := mapType.Key().Underlying()
|
||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||
var llvmKeyType llvm.Type
|
||||
var alg uint64
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// String keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = uint64(tinygo.HashmapAlgorithmString)
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// Trivially comparable keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = uint64(tinygo.HashmapAlgorithmBinary)
|
||||
} else {
|
||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||
// implementation.
|
||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
||||
alg = uint64(tinygo.HashmapAlgorithmInterface)
|
||||
}
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
|
||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
|
||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
||||
if expr.Reserve != nil {
|
||||
sizeHint = b.getValue(expr.Reserve, getPos(expr))
|
||||
var err error
|
||||
@@ -47,10 +33,42 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
}
|
||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
||||
|
||||
// Resolve hash and equal functions for this key type. For string and
|
||||
// binary key types, reference the corresponding runtime functions
|
||||
// directly. For composite types, generate type-specific functions.
|
||||
var hashFn, equalFn llvm.Value
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
hashFn = b.getRuntimeFunctionValue("hashmapStringPtrHash", hashmapKeyHashSignature())
|
||||
equalFn = b.getRuntimeFunctionValue("hashmapStringEqual", hashmapKeyEqualSignature())
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
hashFn = b.getRuntimeFunctionValue("hash32", hashmapKeyHashSignature())
|
||||
equalFn = b.getRuntimeFunctionValue("memequal", hashmapKeyEqualSignature())
|
||||
} else {
|
||||
fn := b.getOrGenerateKeyHashFunc(keyType)
|
||||
hashFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyHashSignature())
|
||||
fn = b.getOrGenerateKeyEqualFunc(keyType)
|
||||
equalFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyEqualSignature())
|
||||
}
|
||||
|
||||
hashmap := b.createRuntimeCall("hashmapMakeGeneric", []llvm.Value{
|
||||
llvmKeySize, llvmValueSize, sizeHint,
|
||||
hashFn, equalFn,
|
||||
}, "")
|
||||
return hashmap, nil
|
||||
}
|
||||
|
||||
// getRuntimeFunctionValue returns a TinyGo function value (with nil context)
|
||||
// for the named runtime function.
|
||||
func (b *builder) getRuntimeFunctionValue(name string, sig *types.Signature) llvm.Value {
|
||||
member := b.program.ImportedPackage("runtime").Members[name]
|
||||
if member == nil {
|
||||
panic("unknown runtime function: " + name)
|
||||
}
|
||||
_, llvmFn := b.getFunction(member.(*ssa.Function))
|
||||
return b.createFuncValue(llvmFn, llvm.ConstNull(b.dataPtrType), sig)
|
||||
}
|
||||
|
||||
// createMapLookup returns the value in a map. It calls a runtime function
|
||||
// depending on the map key type to load the map value and its comma-ok value.
|
||||
func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||
@@ -72,32 +90,23 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
|
||||
|
||||
// Do the lookup. How it is done depends on the key type.
|
||||
var commaOkValue llvm.Value
|
||||
origKeyType := keyType
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
|
||||
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
||||
// growth.
|
||||
} else {
|
||||
// Key stored at actual type: either binary-comparable or with
|
||||
// compiler-generated hash/equal.
|
||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
b.CreateStore(key, mapKeyAlloca)
|
||||
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
|
||||
// Fetch the value from the hashmap.
|
||||
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
|
||||
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||
} else {
|
||||
// Not trivially comparable using memcmp. Make it an interface instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface now.
|
||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||
fnName := "hashmapBinaryGet"
|
||||
if !hashmapIsBinaryKey(keyType) {
|
||||
fnName = "hashmapGenericGet"
|
||||
}
|
||||
params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
|
||||
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
|
||||
commaOkValue = b.createRuntimeCall(fnName, params, "")
|
||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||
}
|
||||
|
||||
// Load the resulting value from the hashmap. The value is set to the zero
|
||||
@@ -120,29 +129,22 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
|
||||
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
||||
b.CreateStore(value, valueAlloca)
|
||||
origKeyType := keyType
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, key, valueAlloca}
|
||||
b.createRuntimeCall("hashmapStringSet", params, "")
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
b.createRuntimeInvoke("hashmapStringSet", params, "")
|
||||
} else {
|
||||
// Key stored at actual type.
|
||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
b.CreateStore(key, keyAlloca)
|
||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
||||
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
||||
b.createRuntimeCall("hashmapBinarySet", params, "")
|
||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||
} else {
|
||||
// Key is not trivially comparable, so compare it as an interface instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface first.
|
||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||
fnName := "hashmapBinarySet"
|
||||
if !hashmapIsBinaryKey(keyType) {
|
||||
fnName = "hashmapGenericSet"
|
||||
}
|
||||
params := []llvm.Value{m, itfKey, valueAlloca}
|
||||
b.createRuntimeCall("hashmapInterfaceSet", params, "")
|
||||
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
||||
b.createRuntimeInvoke(fnName, params, "")
|
||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||
}
|
||||
b.emitLifetimeEnd(valueAlloca, valueSize)
|
||||
}
|
||||
@@ -150,31 +152,23 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
|
||||
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
||||
// function. It is the implementation of the Go delete() builtin.
|
||||
func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||
origKeyType := keyType
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, key}
|
||||
b.createRuntimeCall("hashmapStringDelete", params, "")
|
||||
return nil
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
} else {
|
||||
// Key stored at actual type.
|
||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
b.CreateStore(key, keyAlloca)
|
||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
||||
params := []llvm.Value{m, keyAlloca}
|
||||
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||
return nil
|
||||
} else {
|
||||
// Key is not trivially comparable, so compare it as an interface
|
||||
// instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface first.
|
||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||
fnName := "hashmapBinaryDelete"
|
||||
if !hashmapIsBinaryKey(keyType) {
|
||||
fnName = "hashmapGenericDelete"
|
||||
}
|
||||
params := []llvm.Value{m, itfKey}
|
||||
b.createRuntimeCall("hashmapInterfaceDelete", params, "")
|
||||
params := []llvm.Value{m, keyAlloca}
|
||||
b.createRuntimeCall(fnName, params, "")
|
||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -195,42 +189,15 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
llvmValueType := b.getLLVMType(valueType)
|
||||
|
||||
// There is a special case in which keys are stored as an interface value
|
||||
// instead of the value they normally are. This happens for non-trivially
|
||||
// comparable types such as float32 or some structs.
|
||||
isKeyStoredAsInterface := false
|
||||
if t, ok := keyType.Underlying().(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
} else {
|
||||
// The key is stored as an interface value, and may or may not be an
|
||||
// interface type (for example, float32 keys are stored as an interface
|
||||
// value).
|
||||
if _, ok := keyType.Underlying().(*types.Interface); !ok {
|
||||
isKeyStoredAsInterface = true
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the type of the key as stored in the map.
|
||||
llvmStoredKeyType := llvmKeyType
|
||||
if isKeyStoredAsInterface {
|
||||
llvmStoredKeyType = b.getLLVMRuntimeType("_interface")
|
||||
}
|
||||
// All key types are now stored at their declared type (no interface wrapping).
|
||||
|
||||
// Extract the key and value from the map.
|
||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmKeyType, "range.key")
|
||||
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
||||
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
|
||||
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
||||
mapKey := b.CreateLoad(llvmKeyType, mapKeyAlloca, "")
|
||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||
|
||||
if isKeyStoredAsInterface {
|
||||
// The key is stored as an interface but it isn't of interface type.
|
||||
// Extract the underlying value.
|
||||
mapKey = b.extractValueFromInterface(mapKey, llvmKeyType)
|
||||
}
|
||||
|
||||
// End the lifetimes of the allocas, because we're done with them.
|
||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
|
||||
@@ -250,20 +217,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
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 || keyType.Kind() == types.UnsafePointer
|
||||
case *types.Pointer:
|
||||
return true
|
||||
case *types.Struct:
|
||||
for i := 0; i < keyType.NumFields(); i++ {
|
||||
fieldType := keyType.Field(i).Type().Underlying()
|
||||
if !hashmapIsBinaryKey(fieldType) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Array:
|
||||
return hashmapIsBinaryKey(keyType.Elem())
|
||||
default:
|
||||
@@ -271,68 +227,399 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
|
||||
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
|
||||
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
|
||||
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
|
||||
// we handle nested types. Next, we determine if there are any padding bytes before the next
|
||||
// element and zero those as well.
|
||||
// hashmapKeyHashSignature returns the Go type signature for hashmap key hash
|
||||
// functions: func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||
func hashmapKeyHashSignature() *types.Signature {
|
||||
return types.NewSignatureType(nil, nil, nil,
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "key", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uintptr]),
|
||||
types.NewVar(token.NoPos, nil, "seed", types.Typ[types.Uintptr]),
|
||||
),
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "", types.Typ[types.Uint32]),
|
||||
),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
// hashmapKeyEqualSignature returns the Go type signature for hashmap key equal
|
||||
// functions: func(x, y unsafe.Pointer, n uintptr) bool
|
||||
func hashmapKeyEqualSignature() *types.Signature {
|
||||
return types.NewSignatureType(nil, nil, nil,
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "x", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "y", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "n", types.Typ[types.Uintptr]),
|
||||
),
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "", types.Typ[types.Bool]),
|
||||
),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
switch llvmType.TypeKind() {
|
||||
case llvm.IntegerTypeKind:
|
||||
// no padding bytes
|
||||
return nil
|
||||
case llvm.PointerTypeKind:
|
||||
// mo padding bytes
|
||||
return nil
|
||||
case llvm.ArrayTypeKind:
|
||||
llvmArrayType := llvmType
|
||||
llvmElemType := llvmType.ElementType()
|
||||
// hashmapKeyFuncName returns a canonical name for a generated hash or equal
|
||||
// function based on the key type's underlying structure. Named types are
|
||||
// replaced with their underlying types so that structurally identical key
|
||||
// types (e.g., struct{i1; str1} and struct{i2; str2} where both i1, i2 are
|
||||
// int and str1, str2 are string) share the same generated function.
|
||||
func hashmapKeyFuncName(prefix string, keyType types.Type) string {
|
||||
return prefix + "." + hashmapCanonicalTypeName(keyType)
|
||||
}
|
||||
|
||||
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
|
||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
|
||||
|
||||
// zero any padding bytes in this element
|
||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
||||
// hashmapCanonicalTypeName returns a string representation of the hash/equal
|
||||
// operations needed for a type, stripping named types where the operation does
|
||||
// not depend on the name. Pointer and channel names do not include the element
|
||||
// type because their hash/equal operations only use the pointer word.
|
||||
func hashmapCanonicalTypeName(t types.Type) string {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return t.Name()
|
||||
case *types.Pointer:
|
||||
return "*"
|
||||
case *types.Chan:
|
||||
switch t.Dir() {
|
||||
case types.SendRecv:
|
||||
return "chan"
|
||||
case types.SendOnly:
|
||||
return "chan<-"
|
||||
case types.RecvOnly:
|
||||
return "<-chan"
|
||||
}
|
||||
|
||||
case llvm.StructTypeKind:
|
||||
llvmStructType := llvmType
|
||||
numFields := llvmStructType.StructElementTypesCount()
|
||||
llvmElementTypes := llvmStructType.StructElementTypes()
|
||||
|
||||
for i := 0; i < numFields; i++ {
|
||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
|
||||
|
||||
// zero any padding bytes in this field
|
||||
llvmElemType := llvmElementTypes[i]
|
||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
||||
|
||||
// zero any padding bytes before the next field, if any
|
||||
offset := b.targetData.ElementOffset(llvmStructType, i)
|
||||
storeSize := b.targetData.TypeStoreSize(llvmElemType)
|
||||
fieldEndOffset := offset + storeSize
|
||||
|
||||
var nextOffset uint64
|
||||
if i < numFields-1 {
|
||||
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
|
||||
} else {
|
||||
// Last field? Next offset is the total size of the allocate struct.
|
||||
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
|
||||
}
|
||||
|
||||
if fieldEndOffset != nextOffset {
|
||||
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
|
||||
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
|
||||
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
|
||||
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
|
||||
}
|
||||
case *types.Interface:
|
||||
if t.NumMethods() == 0 {
|
||||
return "interface{}"
|
||||
}
|
||||
return t.String()
|
||||
case *types.Struct:
|
||||
s := "struct{"
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
if i > 0 {
|
||||
s += "; "
|
||||
}
|
||||
s += hashmapCanonicalTypeName(t.Field(i).Type())
|
||||
}
|
||||
return s + "}"
|
||||
case *types.Array:
|
||||
return fmt.Sprintf("[%d]%s", t.Len(), hashmapCanonicalTypeName(t.Elem()))
|
||||
}
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// getOrGenerateKeyHashFunc returns an LLVM function that computes the hash
|
||||
// of a key of the given type. The function is generated on first call and
|
||||
// cached in the module.
|
||||
func (b *builder) getOrGenerateKeyHashFunc(keyType types.Type) llvm.Value {
|
||||
name := hashmapKeyFuncName("hashmapKeyHash", keyType)
|
||||
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||
return fn
|
||||
}
|
||||
|
||||
return nil
|
||||
// Create the LLVM function type:
|
||||
// (key ptr, size uintptr, seed uintptr, context ptr) -> i32
|
||||
fnType := llvm.FunctionType(b.ctx.Int32Type(), []llvm.Type{
|
||||
b.dataPtrType, b.uintptrType, b.uintptrType, b.dataPtrType,
|
||||
}, false)
|
||||
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
fn.SetUnnamedAddr(true)
|
||||
b.addStandardAttributes(fn)
|
||||
|
||||
// Generate the function body.
|
||||
savedBlock := b.GetInsertBlock()
|
||||
defer b.SetInsertPointAtEnd(savedBlock)
|
||||
|
||||
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
keyPtr := fn.Param(0)
|
||||
seed := fn.Param(2)
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
hash := b.generateKeyHash(keyType, llvmKeyType, keyPtr, seed)
|
||||
b.CreateRet(hash)
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
// getOrGenerateKeyEqualFunc returns an LLVM function that compares two keys
|
||||
// of the given type for equality. The function is generated on first call
|
||||
// and cached in the module.
|
||||
func (b *builder) getOrGenerateKeyEqualFunc(keyType types.Type) llvm.Value {
|
||||
name := hashmapKeyFuncName("hashmapKeyEqual", keyType)
|
||||
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||
return fn
|
||||
}
|
||||
|
||||
// Create the LLVM function type:
|
||||
// (x ptr, y ptr, n uintptr, context ptr) -> i1
|
||||
fnType := llvm.FunctionType(b.ctx.Int1Type(), []llvm.Type{
|
||||
b.dataPtrType, b.dataPtrType, b.uintptrType, b.dataPtrType,
|
||||
}, false)
|
||||
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
fn.SetUnnamedAddr(true)
|
||||
b.addStandardAttributes(fn)
|
||||
|
||||
// Generate the function body.
|
||||
savedBlock := b.GetInsertBlock()
|
||||
defer b.SetInsertPointAtEnd(savedBlock)
|
||||
|
||||
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
xPtr := fn.Param(0)
|
||||
yPtr := fn.Param(1)
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
result := b.generateKeyEqual(keyType, llvmKeyType, xPtr, yPtr, fn)
|
||||
b.CreateRet(result)
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
// generateKeyHash generates IR that hashes a key value. Returns the i32 hash.
|
||||
func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, keyPtr llvm.Value, seed llvm.Value) llvm.Value {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
// Hash the string contents. The size parameter is unused by
|
||||
// hashmapStringPtrHash (it dereferences the string header to
|
||||
// get the actual length), but we pass it for signature
|
||||
// consistency with other hash functions.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hashmapStringPtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
}
|
||||
if keyType.Info()&types.IsFloat != 0 {
|
||||
// Float hash: normalizes -0 to +0 before hashing.
|
||||
if keyType.Kind() == types.Float32 {
|
||||
return b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||
}
|
||||
return b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||
}
|
||||
if keyType.Info()&types.IsComplex != 0 {
|
||||
// Complex hash: hash real and imaginary parts as floats.
|
||||
if keyType.Kind() == types.Complex64 {
|
||||
realPtr := keyPtr
|
||||
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||
llvm.ConstInt(b.uintptrType, 4, false),
|
||||
}, "")
|
||||
realHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||
imagHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||
return b.CreateXor(realHash, imagHash, "")
|
||||
}
|
||||
realPtr := keyPtr
|
||||
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||
llvm.ConstInt(b.uintptrType, 8, false),
|
||||
}, "")
|
||||
realHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||
imagHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||
return b.CreateXor(realHash, imagHash, "")
|
||||
}
|
||||
// Integer/boolean: hash the raw bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
case *types.Pointer, *types.Chan:
|
||||
// Pointers and channels: hash as raw pointer-sized bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
case *types.Interface:
|
||||
// Interface: use runtime reflection-based hash.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hashmapInterfacePtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
case *types.Struct:
|
||||
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < keyType.NumFields(); i++ {
|
||||
if keyType.Field(i).Name() == "_" {
|
||||
continue // blank fields are ignored in Go equality
|
||||
}
|
||||
fieldType := keyType.Field(i).Type()
|
||||
llvmFieldType := b.getLLVMType(fieldType)
|
||||
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||
continue // skip zero-sized fields
|
||||
}
|
||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||
fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||
fieldHash := b.generateKeyHash(fieldType, llvmFieldType, fieldPtr, seed)
|
||||
hash = b.CreateXor(hash, fieldHash, "")
|
||||
}
|
||||
return hash
|
||||
case *types.Array:
|
||||
elemType := keyType.Elem()
|
||||
llvmElemType := b.getLLVMType(elemType)
|
||||
arrayLen := keyType.Len()
|
||||
if hashmapIsBinaryKey(elemType) {
|
||||
// All elements are binary-comparable; hash the entire array as raw bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
}
|
||||
if arrayLen == 0 {
|
||||
return llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
}
|
||||
if arrayLen <= hashArrayUnrollLimit {
|
||||
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < int(arrayLen); i++ {
|
||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||
hash = b.CreateXor(hash, elemHash, "")
|
||||
}
|
||||
return hash
|
||||
}
|
||||
initHash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
|
||||
loopEntry := b.GetInsertBlock()
|
||||
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.body")
|
||||
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.done")
|
||||
|
||||
b.CreateBr(loopBody)
|
||||
b.SetInsertPointAtEnd(loopBody)
|
||||
|
||||
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||
phiHash := b.CreatePHI(b.ctx.Int32Type(), "hash.acc")
|
||||
|
||||
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, phiI}, "")
|
||||
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||
newHash := b.CreateXor(phiHash, elemHash, "")
|
||||
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||
cond := b.CreateICmp(llvm.IntULT, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||
b.CreateCondBr(cond, loopBody, loopDone)
|
||||
|
||||
bodyEnd := b.GetInsertBlock()
|
||||
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||
phiHash.AddIncoming([]llvm.Value{initHash, newHash},
|
||||
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||
|
||||
b.SetInsertPointAtEnd(loopDone)
|
||||
return newHash
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled key type for hash generation: %T", keyType))
|
||||
}
|
||||
}
|
||||
|
||||
// generateKeyEqual generates IR that compares two key values for equality.
|
||||
// Returns an i1 result.
|
||||
func (b *builder) generateKeyEqual(keyType types.Type, llvmKeyType llvm.Type, xPtr, yPtr llvm.Value, fn llvm.Value) llvm.Value {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
// Compare strings: load both string headers and compare.
|
||||
xStr := b.CreateLoad(llvmKeyType, xPtr, "x.str")
|
||||
yStr := b.CreateLoad(llvmKeyType, yPtr, "y.str")
|
||||
return b.createRuntimeCall("stringEqual", []llvm.Value{xStr, yStr}, "eq")
|
||||
}
|
||||
if keyType.Info()&types.IsFloat != 0 {
|
||||
// Float equality: fcmp oeq handles -0==+0 (true) and NaN==NaN (false).
|
||||
xVal := b.CreateLoad(llvmKeyType, xPtr, "x.float")
|
||||
yVal := b.CreateLoad(llvmKeyType, yPtr, "y.float")
|
||||
return b.CreateFCmp(llvm.FloatOEQ, xVal, yVal, "eq")
|
||||
}
|
||||
if keyType.Info()&types.IsComplex != 0 {
|
||||
// Complex equality: both real and imaginary parts must be equal.
|
||||
var floatType llvm.Type
|
||||
if keyType.Kind() == types.Complex64 {
|
||||
floatType = b.ctx.FloatType()
|
||||
} else {
|
||||
floatType = b.ctx.DoubleType()
|
||||
}
|
||||
floatSize := b.targetData.TypeAllocSize(floatType)
|
||||
imagOffset := llvm.ConstInt(b.uintptrType, floatSize, false)
|
||||
// Real parts
|
||||
xReal := b.CreateLoad(floatType, xPtr, "x.real")
|
||||
yReal := b.CreateLoad(floatType, yPtr, "y.real")
|
||||
realEq := b.CreateFCmp(llvm.FloatOEQ, xReal, yReal, "eq.real")
|
||||
// Imaginary parts
|
||||
xImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), xPtr, []llvm.Value{imagOffset}, "")
|
||||
yImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), yPtr, []llvm.Value{imagOffset}, "")
|
||||
xImag := b.CreateLoad(floatType, xImagPtr, "x.imag")
|
||||
yImag := b.CreateLoad(floatType, yImagPtr, "y.imag")
|
||||
imagEq := b.CreateFCmp(llvm.FloatOEQ, xImag, yImag, "eq.imag")
|
||||
return b.CreateAnd(realEq, imagEq, "")
|
||||
}
|
||||
// Integer/boolean: compare raw bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
case *types.Pointer, *types.Chan:
|
||||
// Pointers and channels: compare as raw pointer-sized bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
case *types.Interface:
|
||||
// Interface: use runtime interface equality.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hashmapInterfaceEqual", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
case *types.Struct:
|
||||
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false) // start with true
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < keyType.NumFields(); i++ {
|
||||
if keyType.Field(i).Name() == "_" {
|
||||
continue // blank fields are ignored in Go equality
|
||||
}
|
||||
fieldType := keyType.Field(i).Type()
|
||||
llvmFieldType := b.getLLVMType(fieldType)
|
||||
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||
continue // skip zero-sized fields
|
||||
}
|
||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||
xFieldPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||
yFieldPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||
fieldEq := b.generateKeyEqual(fieldType, llvmFieldType, xFieldPtr, yFieldPtr, fn)
|
||||
result = b.CreateAnd(result, fieldEq, "")
|
||||
}
|
||||
return result
|
||||
case *types.Array:
|
||||
elemType := keyType.Elem()
|
||||
llvmElemType := b.getLLVMType(elemType)
|
||||
arrayLen := keyType.Len()
|
||||
if hashmapIsBinaryKey(elemType) {
|
||||
// All elements are binary-comparable; compare the entire array.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
}
|
||||
if arrayLen == 0 {
|
||||
return llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||
}
|
||||
if arrayLen <= hashArrayUnrollLimit {
|
||||
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < int(arrayLen); i++ {
|
||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||
result = b.CreateAnd(result, elemEq, "")
|
||||
}
|
||||
return result
|
||||
}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
|
||||
loopEntry := b.GetInsertBlock()
|
||||
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.body")
|
||||
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.done")
|
||||
|
||||
b.CreateBr(loopBody)
|
||||
b.SetInsertPointAtEnd(loopBody)
|
||||
|
||||
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||
|
||||
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, phiI}, "")
|
||||
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, phiI}, "")
|
||||
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||
|
||||
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||
atEnd := b.CreateICmp(llvm.IntUGE, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||
exitLoop := b.CreateOr(atEnd, b.CreateNot(elemEq, ""), "")
|
||||
b.CreateCondBr(exitLoop, loopDone, loopBody)
|
||||
|
||||
bodyEnd := b.GetInsertBlock()
|
||||
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||
|
||||
b.SetInsertPointAtEnd(loopDone)
|
||||
return elemEq
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled key type for equal generation: %T", keyType))
|
||||
}
|
||||
}
|
||||
|
||||
+30
-1
@@ -34,6 +34,7 @@ type functionInfo struct {
|
||||
interrupt bool // go:interrupt
|
||||
nobounds bool // go:nobounds
|
||||
noescape bool // go:noescape
|
||||
noheap bool // go:noheap
|
||||
variadic bool // go:variadic (CGo only)
|
||||
inline inlineType // go:inline
|
||||
}
|
||||
@@ -161,7 +162,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
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", "runtime.alloc_noheap", "runtime.alloc_zero":
|
||||
// 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.
|
||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||
@@ -190,6 +191,31 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
case "runtime.stringFromRunes":
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.hashmapSet":
|
||||
// The key (param 2) and value (param 3) pointers are only read via
|
||||
// memcpy/hash/equal and are never captured. The indirect calls
|
||||
// through m.keyHash and m.keyEqual function pointers prevent LLVM's
|
||||
// functionattrs pass from inferring this automatically.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.hashmapGet":
|
||||
// The key (param 2) is read-only and never captured.
|
||||
// The value (param 3) is written to (receives the result) but never captured.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.hashmapDelete":
|
||||
// The key (param 2) is read-only and never captured.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.hashmapGenericSet":
|
||||
// Same as hashmapBinarySet: key (param 2) and value (param 3) are
|
||||
// not captured.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.hashmapGenericGet":
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.hashmapGenericDelete":
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.trackPointer":
|
||||
// This function is necessary for tracking pointers on the stack in a
|
||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||
@@ -451,6 +477,9 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
if len(f.Blocks) == 0 {
|
||||
info.noescape = true
|
||||
}
|
||||
case "//go:noheap":
|
||||
// Ensure this function does not allocate on the heap.
|
||||
info.noheap = true
|
||||
case "//go:variadic":
|
||||
// The //go:variadic pragma is emitted by the CGo preprocessing
|
||||
// pass for C variadic functions. This includes both explicit
|
||||
|
||||
Vendored
+29
-33
@@ -10,33 +10,30 @@ target triple = "wasm32-unknown-wasi"
|
||||
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = add i32 %x, %y
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -50,14 +47,14 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.divideByZeroPanic(ptr) #1
|
||||
declare void @runtime.divideByZeroPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -67,12 +64,12 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -86,12 +83,12 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -101,66 +98,66 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp oeq float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp une float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp olt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp ole float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp ogt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp oge float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
|
||||
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float %x.r
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
|
||||
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float %x.i
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fadd float %x.r, %y.r
|
||||
%1 = fadd float %x.i, %y.i
|
||||
@@ -170,7 +167,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fsub float %x.r, %y.r
|
||||
%1 = fsub float %x.i, %y.i
|
||||
@@ -180,7 +177,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fmul float %x.r, %y.r
|
||||
%1 = fmul float %x.i, %y.i
|
||||
@@ -194,19 +191,18 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.foo(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
|
||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+21
-25
@@ -6,76 +6,73 @@ target triple = "wasm32-unknown-wasi"
|
||||
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
||||
%runtime.chanSelectState = type { ptr, ptr }
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
store i32 3, ptr %chan.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
|
||||
|
||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||
%select.send.value = alloca i32, align 4
|
||||
@@ -88,7 +85,7 @@ entry:
|
||||
store ptr %ch2, ptr %0, align 4
|
||||
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
|
||||
store ptr null, ptr %.repack3, align 4
|
||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #4
|
||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||
%2 = icmp eq i32 %1, 0
|
||||
@@ -105,10 +102,9 @@ select.body: ; preds = %select.next
|
||||
br label %select.done
|
||||
}
|
||||
|
||||
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1
|
||||
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #0
|
||||
|
||||
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 #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 willreturn memory(argmem: readwrite) }
|
||||
attributes #4 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
+35
-38
@@ -3,32 +3,29 @@ source_filename = "defer.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
|
||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface, ptr }
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.external(ptr) #2
|
||||
declare void @main.external(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.deferSimple(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%defer.alloca = alloca { i32, ptr }, align 4
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||
store i32 0, ptr %defer.alloca, align 4
|
||||
%defer.alloca.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||
store ptr null, ptr %defer.alloca.repack15, align 4
|
||||
store ptr %defer.next, ptr %defer.alloca.repack15, align 4
|
||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
@@ -112,14 +109,14 @@ rundefers.end3: ; preds = %rundefers.loophead6
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
||||
declare ptr @llvm.stacksave.p0() #3
|
||||
declare ptr @llvm.stacksave.p0() #2
|
||||
|
||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2
|
||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(28), ptr, ptr) #1
|
||||
|
||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
|
||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(28), ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
call void @runtime.printlock(ptr undef) #4
|
||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||
@@ -127,29 +124,29 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.printlock(ptr) #2
|
||||
declare void @runtime.printlock(ptr) #1
|
||||
|
||||
declare void @runtime.printint32(i32, ptr) #2
|
||||
declare void @runtime.printint32(i32, ptr) #1
|
||||
|
||||
declare void @runtime.printunlock(ptr) #2
|
||||
declare void @runtime.printunlock(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%defer.alloca2 = alloca { i32, ptr }, align 4
|
||||
%defer.alloca = alloca { i32, ptr }, align 4
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||
store i32 0, ptr %defer.alloca, align 4
|
||||
%defer.alloca.repack22 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||
store ptr null, ptr %defer.alloca.repack22, align 4
|
||||
store ptr %defer.next, ptr %defer.alloca.repack22, align 4
|
||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||
store i32 1, ptr %defer.alloca2, align 4
|
||||
%defer.alloca2.repack23 = getelementptr inbounds nuw i8, ptr %defer.alloca2, i32 4
|
||||
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
|
||||
%defer.alloca2.repack24 = getelementptr inbounds nuw i8, ptr %defer.alloca2, i32 4
|
||||
store ptr %defer.alloca, ptr %defer.alloca2.repack24, align 4
|
||||
store ptr %defer.alloca2, ptr %deferPtr, align 4
|
||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
@@ -253,7 +250,7 @@ rundefers.end7: ; preds = %rundefers.loophead1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
call void @runtime.printlock(ptr undef) #4
|
||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||
@@ -262,7 +259,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
call void @runtime.printlock(ptr undef) #4
|
||||
call void @runtime.printint32(i32 5, ptr undef) #4
|
||||
@@ -271,11 +268,10 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
br label %for.body
|
||||
@@ -315,12 +311,14 @@ rundefers.end: ; preds = %rundefers.loophead
|
||||
br label %recover
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.deferLoop(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
br label %for.loop
|
||||
@@ -405,12 +403,11 @@ rundefers.end1: ; preds = %rundefers.loophead4
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #0 {
|
||||
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
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
br label %for.loop
|
||||
@@ -505,9 +502,9 @@ 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 #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 #3 = { nocallback nofree nosync nounwind willreturn }
|
||||
attributes #0 = { 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 = { "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 = { nocallback nofree nosync nounwind willreturn }
|
||||
attributes #3 = { 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 #4 = { nounwind }
|
||||
attributes #5 = { nounwind returns_twice }
|
||||
|
||||
Vendored
+12
-16
@@ -3,19 +3,16 @@ source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -27,25 +24,25 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.maxu32f(ptr %context) unnamed_addr #2 {
|
||||
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float 0x41F0000000000000
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 -1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #2 {
|
||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = uitofp i32 %v to float
|
||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||
@@ -55,7 +52,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #2 {
|
||||
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -68,7 +65,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #2 {
|
||||
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||
@@ -80,7 +77,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #2 {
|
||||
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||
@@ -93,6 +90,5 @@ entry:
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
|
||||
Vendored
+11
-15
@@ -3,48 +3,44 @@ source_filename = "func.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq ptr %callback.funcptr, null
|
||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||
|
||||
fpcall.next: ; preds = %entry
|
||||
call void %callback.funcptr(i32 3, ptr %callback.context) #3
|
||||
call void %callback.funcptr(i32 3, ptr %callback.context) #2
|
||||
ret void
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(ptr undef) #3
|
||||
call void @runtime.nilPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.nilPanic(ptr) #1
|
||||
declare void @runtime.nilPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.bar(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+18
-15
@@ -25,19 +25,16 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"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
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
@@ -55,8 +52,11 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
@@ -72,10 +72,10 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
%new = call align 1 ptr @runtime.alloc_zero(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store ptr %new, ptr @main.struct1, align 4
|
||||
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
@@ -93,8 +93,11 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc_zero(i32, ptr, ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
|
||||
@@ -103,7 +106,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
@@ -125,7 +128,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
|
||||
@@ -139,7 +142,7 @@ entry:
|
||||
ret %runtime._interface %1
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { 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 #3 = { nounwind }
|
||||
|
||||
Vendored
+100
-212
@@ -1,259 +1,147 @@
|
||||
; ModuleID = 'generics.go'
|
||||
source_filename = "generics.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%"main.Point[int]" = type { i32, i32 }
|
||||
%"main.Point[float32]" = type { float, float }
|
||||
%"main.Point[int]" = type { i32, i32 }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.main(i8* %context) unnamed_addr #1 {
|
||||
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%bi = alloca %"main.Point[int]", align 8
|
||||
%ai = alloca %"main.Point[int]", align 8
|
||||
%bf = alloca %"main.Point[float32]", align 8
|
||||
%af = alloca %"main.Point[float32]", align 8
|
||||
%af.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
||||
store float 0.000000e+00, float* %af.repack, align 8
|
||||
%af.repack1 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
||||
store float 0.000000e+00, float* %af.repack1, align 4
|
||||
%0 = bitcast %"main.Point[float32]"* %af to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||
%bf.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
||||
store float 0.000000e+00, float* %bf.repack, align 8
|
||||
%bf.repack2 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
||||
store float 0.000000e+00, float* %bf.repack2, align 4
|
||||
%1 = bitcast %"main.Point[float32]"* %bf to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
||||
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
||||
%.unpack = load float, float* %.elt, align 8
|
||||
%.elt3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
||||
%.unpack4 = load float, float* %.elt3, align 4
|
||||
%.elt5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
||||
%.unpack6 = load float, float* %.elt5, align 8
|
||||
%.elt7 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
||||
%.unpack8 = load float, float* %.elt7, align 4
|
||||
%2 = call %"main.Point[float32]" @"main.Add[float32]"(float %.unpack, float %.unpack4, float %.unpack6, float %.unpack8, i8* undef)
|
||||
%ai.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
||||
store i32 0, i32* %ai.repack, align 8
|
||||
%ai.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
||||
store i32 0, i32* %ai.repack9, align 4
|
||||
%3 = bitcast %"main.Point[int]"* %ai to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %3, i8* undef) #2
|
||||
%bi.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
||||
store i32 0, i32* %bi.repack, align 8
|
||||
%bi.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
||||
store i32 0, i32* %bi.repack10, align 4
|
||||
%4 = bitcast %"main.Point[int]"* %bi to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %4, i8* undef) #2
|
||||
%.elt11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
||||
%.unpack12 = load i32, i32* %.elt11, align 8
|
||||
%.elt13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
||||
%.unpack14 = load i32, i32* %.elt13, align 4
|
||||
%.elt15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
||||
%.unpack16 = load i32, i32* %.elt15, align 8
|
||||
%.elt17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
||||
%.unpack18 = load i32, i32* %.elt17, align 4
|
||||
%5 = call %"main.Point[int]" @"main.Add[int]"(i32 %.unpack12, i32 %.unpack14, i32 %.unpack16, i32 %.unpack18, i8* undef)
|
||||
%0 = call %"main.Point[float32]" @"main.Add[float32]"(float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, ptr undef)
|
||||
%1 = call %"main.Point[int]" @"main.Add[int]"(i32 0, i32 0, i32 0, i32 0, ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, i8* %context) unnamed_addr #1 {
|
||||
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%complit = alloca %"main.Point[float32]", align 8
|
||||
%b = alloca %"main.Point[float32]", align 8
|
||||
%a = alloca %"main.Point[float32]", align 8
|
||||
%a.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
||||
store float 0.000000e+00, float* %a.repack, align 8
|
||||
%a.repack9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
||||
store float 0.000000e+00, float* %a.repack9, align 4
|
||||
%0 = bitcast %"main.Point[float32]"* %a to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||
%a.repack10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
||||
store float %a.X, float* %a.repack10, align 8
|
||||
%a.repack11 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
||||
store float %a.Y, float* %a.repack11, align 4
|
||||
%b.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
||||
store float 0.000000e+00, float* %b.repack, align 8
|
||||
%b.repack13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
||||
store float 0.000000e+00, float* %b.repack13, align 4
|
||||
%1 = bitcast %"main.Point[float32]"* %b to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
||||
%b.repack14 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
||||
store float %b.X, float* %b.repack14, align 8
|
||||
%b.repack15 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
||||
store float %b.Y, float* %b.repack15, align 4
|
||||
call void @main.checkSize(i32 4, i8* undef) #2
|
||||
call void @main.checkSize(i32 8, i8* undef) #2
|
||||
%complit.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
||||
store float 0.000000e+00, float* %complit.repack, align 8
|
||||
%complit.repack17 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
||||
store float 0.000000e+00, float* %complit.repack17, align 4
|
||||
%2 = bitcast %"main.Point[float32]"* %complit to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
||||
%3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
||||
%stackalloc = alloca i8, align 1
|
||||
%a = 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 %a, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store float %a.X, ptr %a, align 4
|
||||
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
store float %a.Y, ptr %a.repack5, align 4
|
||||
%b = 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 %b, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store float %b.X, ptr %b, align 4
|
||||
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
store float %b.Y, ptr %b.repack7, align 4
|
||||
call void @main.checkSize(i32 4, ptr undef) #3
|
||||
call void @main.checkSize(i32 8, ptr undef) #3
|
||||
%complit = 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 %complit, ptr nonnull %stackalloc, ptr undef) #3
|
||||
br i1 false, label %deref.throw, label %deref.next
|
||||
|
||||
deref.next: ; preds = %entry
|
||||
br i1 false, label %deref.throw1, label %deref.next2
|
||||
br i1 false, label %deref.throw, label %deref.next1
|
||||
|
||||
deref.next2: ; preds = %deref.next
|
||||
%4 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
||||
%5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
||||
%6 = load float, float* %5, align 8
|
||||
%7 = load float, float* %4, align 8
|
||||
%8 = fadd float %6, %7
|
||||
br i1 false, label %deref.throw3, label %deref.next4
|
||||
deref.next1: ; preds = %deref.next
|
||||
%0 = load float, ptr %a, align 4
|
||||
%1 = load float, ptr %b, align 4
|
||||
%2 = fadd float %0, %1
|
||||
br i1 false, label %deref.throw, label %deref.next2
|
||||
|
||||
deref.next4: ; preds = %deref.next2
|
||||
br i1 false, label %deref.throw5, label %deref.next6
|
||||
deref.next2: ; preds = %deref.next1
|
||||
br i1 false, label %deref.throw, label %deref.next3
|
||||
|
||||
deref.next6: ; preds = %deref.next4
|
||||
%9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
||||
%10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
||||
%11 = load float, float* %10, align 4
|
||||
%12 = load float, float* %9, align 4
|
||||
br i1 false, label %store.throw, label %store.next
|
||||
deref.next3: ; preds = %deref.next2
|
||||
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
%5 = load float, ptr %4, align 4
|
||||
%6 = load float, ptr %3, align 4
|
||||
br i1 false, label %deref.throw, label %store.next
|
||||
|
||||
store.next: ; preds = %deref.next6
|
||||
store float %8, float* %3, align 8
|
||||
br i1 false, label %store.throw7, label %store.next8
|
||||
store.next: ; preds = %deref.next3
|
||||
store float %2, ptr %complit, align 4
|
||||
br i1 false, label %deref.throw, label %store.next4
|
||||
|
||||
store.next8: ; preds = %store.next
|
||||
%13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
||||
%14 = fadd float %11, %12
|
||||
store float %14, float* %13, align 4
|
||||
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
||||
%.unpack = load float, float* %.elt, align 8
|
||||
%15 = insertvalue %"main.Point[float32]" undef, float %.unpack, 0
|
||||
%16 = insertvalue %"main.Point[float32]" %15, float %14, 1
|
||||
ret %"main.Point[float32]" %16
|
||||
store.next4: ; preds = %store.next
|
||||
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||
%8 = fadd float %5, %6
|
||||
store float %8, ptr %7, align 4
|
||||
%.unpack = load float, ptr %complit, align 4
|
||||
%9 = insertvalue %"main.Point[float32]" poison, float %.unpack, 0
|
||||
%10 = insertvalue %"main.Point[float32]" %9, float %8, 1
|
||||
ret %"main.Point[float32]" %10
|
||||
|
||||
deref.throw: ; preds = %entry
|
||||
unreachable
|
||||
|
||||
deref.throw1: ; preds = %deref.next
|
||||
unreachable
|
||||
|
||||
deref.throw3: ; preds = %deref.next2
|
||||
unreachable
|
||||
|
||||
deref.throw5: ; preds = %deref.next4
|
||||
unreachable
|
||||
|
||||
store.throw: ; preds = %deref.next6
|
||||
unreachable
|
||||
|
||||
store.throw7: ; preds = %store.next
|
||||
deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @main.checkSize(i32, i8*) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||
|
||||
declare void @runtime.nilPanic(i8*) #0
|
||||
declare void @main.checkSize(i32, ptr) #0
|
||||
|
||||
declare void @runtime.nilPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, i8* %context) unnamed_addr #1 {
|
||||
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%complit = alloca %"main.Point[int]", align 8
|
||||
%b = alloca %"main.Point[int]", align 8
|
||||
%a = alloca %"main.Point[int]", align 8
|
||||
%a.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
||||
store i32 0, i32* %a.repack, align 8
|
||||
%a.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
||||
store i32 0, i32* %a.repack9, align 4
|
||||
%0 = bitcast %"main.Point[int]"* %a to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||
%a.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
||||
store i32 %a.X, i32* %a.repack10, align 8
|
||||
%a.repack11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
||||
store i32 %a.Y, i32* %a.repack11, align 4
|
||||
%b.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
||||
store i32 0, i32* %b.repack, align 8
|
||||
%b.repack13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
||||
store i32 0, i32* %b.repack13, align 4
|
||||
%1 = bitcast %"main.Point[int]"* %b to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
||||
%b.repack14 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
||||
store i32 %b.X, i32* %b.repack14, align 8
|
||||
%b.repack15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
||||
store i32 %b.Y, i32* %b.repack15, align 4
|
||||
call void @main.checkSize(i32 4, i8* undef) #2
|
||||
call void @main.checkSize(i32 8, i8* undef) #2
|
||||
%complit.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
||||
store i32 0, i32* %complit.repack, align 8
|
||||
%complit.repack17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
||||
store i32 0, i32* %complit.repack17, align 4
|
||||
%2 = bitcast %"main.Point[int]"* %complit to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
||||
%3 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
||||
%stackalloc = alloca i8, align 1
|
||||
%a = 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 %a, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store i32 %a.X, ptr %a, align 4
|
||||
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
store i32 %a.Y, ptr %a.repack5, align 4
|
||||
%b = 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 %b, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store i32 %b.X, ptr %b, align 4
|
||||
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
store i32 %b.Y, ptr %b.repack7, align 4
|
||||
call void @main.checkSize(i32 4, ptr undef) #3
|
||||
call void @main.checkSize(i32 8, ptr undef) #3
|
||||
%complit = 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 %complit, ptr nonnull %stackalloc, ptr undef) #3
|
||||
br i1 false, label %deref.throw, label %deref.next
|
||||
|
||||
deref.next: ; preds = %entry
|
||||
br i1 false, label %deref.throw1, label %deref.next2
|
||||
br i1 false, label %deref.throw, label %deref.next1
|
||||
|
||||
deref.next2: ; preds = %deref.next
|
||||
%4 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
||||
%5 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
||||
%6 = load i32, i32* %5, align 8
|
||||
%7 = load i32, i32* %4, align 8
|
||||
%8 = add i32 %6, %7
|
||||
br i1 false, label %deref.throw3, label %deref.next4
|
||||
deref.next1: ; preds = %deref.next
|
||||
%0 = load i32, ptr %a, align 4
|
||||
%1 = load i32, ptr %b, align 4
|
||||
%2 = add i32 %0, %1
|
||||
br i1 false, label %deref.throw, label %deref.next2
|
||||
|
||||
deref.next4: ; preds = %deref.next2
|
||||
br i1 false, label %deref.throw5, label %deref.next6
|
||||
deref.next2: ; preds = %deref.next1
|
||||
br i1 false, label %deref.throw, label %deref.next3
|
||||
|
||||
deref.next6: ; preds = %deref.next4
|
||||
%9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
||||
%10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
||||
%11 = load i32, i32* %10, align 4
|
||||
%12 = load i32, i32* %9, align 4
|
||||
br i1 false, label %store.throw, label %store.next
|
||||
deref.next3: ; preds = %deref.next2
|
||||
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
%5 = load i32, ptr %4, align 4
|
||||
%6 = load i32, ptr %3, align 4
|
||||
br i1 false, label %deref.throw, label %store.next
|
||||
|
||||
store.next: ; preds = %deref.next6
|
||||
store i32 %8, i32* %3, align 8
|
||||
br i1 false, label %store.throw7, label %store.next8
|
||||
store.next: ; preds = %deref.next3
|
||||
store i32 %2, ptr %complit, align 4
|
||||
br i1 false, label %deref.throw, label %store.next4
|
||||
|
||||
store.next8: ; preds = %store.next
|
||||
%13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
||||
%14 = add i32 %11, %12
|
||||
store i32 %14, i32* %13, align 4
|
||||
%.elt = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
||||
%.unpack = load i32, i32* %.elt, align 8
|
||||
%15 = insertvalue %"main.Point[int]" undef, i32 %.unpack, 0
|
||||
%16 = insertvalue %"main.Point[int]" %15, i32 %14, 1
|
||||
ret %"main.Point[int]" %16
|
||||
store.next4: ; preds = %store.next
|
||||
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||
%8 = add i32 %5, %6
|
||||
store i32 %8, ptr %7, align 4
|
||||
%.unpack = load i32, ptr %complit, align 4
|
||||
%9 = insertvalue %"main.Point[int]" poison, i32 %.unpack, 0
|
||||
%10 = insertvalue %"main.Point[int]" %9, i32 %8, 1
|
||||
ret %"main.Point[int]" %10
|
||||
|
||||
deref.throw: ; preds = %entry
|
||||
unreachable
|
||||
|
||||
deref.throw1: ; preds = %deref.next
|
||||
unreachable
|
||||
|
||||
deref.throw3: ; preds = %deref.next2
|
||||
unreachable
|
||||
|
||||
deref.throw5: ; preds = %deref.next4
|
||||
unreachable
|
||||
|
||||
store.throw: ; preds = %deref.next6
|
||||
unreachable
|
||||
|
||||
store.throw7: ; preds = %store.next
|
||||
deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { 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 #3 = { nounwind }
|
||||
|
||||
Vendored
+13
-17
@@ -5,27 +5,24 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime._string = type { ptr, i32 }
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%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) #2
|
||||
ret ptr %s.data
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp slt i16 %len, 0
|
||||
@@ -39,25 +36,24 @@ unsafe.String.next: ; preds = %entry
|
||||
%5 = zext nneg i16 %len to i32
|
||||
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
||||
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||
ret %runtime._string %7
|
||||
|
||||
unsafe.String.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||
|
||||
; 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 readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%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) #2
|
||||
ret ptr %s.data
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+39
-43
@@ -5,35 +5,32 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime._string = type { ptr, i32 }
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.smin.i32(i32, i32) #3
|
||||
declare i32 @llvm.smin.i32(i32, i32) #2
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||
@@ -41,7 +38,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||
@@ -50,109 +47,109 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
|
||||
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i8 @llvm.umin.i8(i8, i8) #3
|
||||
declare i8 @llvm.umin.i8(i8, i8) #2
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.umin.i32(i32, i32) #3
|
||||
declare i32 @llvm.umin.i32(i32, i32) #2
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%0 = call float @llvm.minimum.f32(float %a, float %b)
|
||||
ret float %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare float @llvm.minimum.f32(float, float) #3
|
||||
declare float @llvm.minimum.f32(float, float) #2
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%0 = call double @llvm.minimum.f64(double %a, double %b)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare double @llvm.minimum.f64(double, double) #3
|
||||
declare double @llvm.minimum.f64(double, double) #2
|
||||
|
||||
; 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 readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||
%stackalloc = alloca i8, align 1
|
||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
|
||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #4
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.smax.i32(i32, i32) #3
|
||||
declare i32 @llvm.smax.i32(i32, i32) #2
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.umax.i32(i32, i32) #3
|
||||
declare i32 @llvm.umax.i32(i32, i32) #2
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%0 = call float @llvm.maximum.f32(float %a, float %b)
|
||||
ret float %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare float @llvm.maximum.f32(float, float) #3
|
||||
declare float @llvm.maximum.f32(float, float) #2
|
||||
|
||||
; 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 readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||
%stackalloc = alloca i8, align 1
|
||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
|
||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #4
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = shl i32 %s.len, 2
|
||||
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
||||
@@ -160,26 +157,25 @@ entry:
|
||||
}
|
||||
|
||||
; 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
|
||||
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 #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.clearMap(ptr dereferenceable_or_null(48) %m, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
|
||||
call void @runtime.hashmapClear(ptr %m, ptr undef) #4
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(48), ptr) #0
|
||||
|
||||
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 #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 #4 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||
attributes #5 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||
attributes #4 = { nounwind }
|
||||
|
||||
+28
-28
@@ -5,39 +5,36 @@ target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), 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) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, ptr) #2
|
||||
declare void @main.regularFunction(i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #2
|
||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #1
|
||||
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), 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) #11
|
||||
@@ -45,13 +42,13 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||
@@ -59,7 +56,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
||||
store i32 3, ptr %n, align 4
|
||||
@@ -76,8 +73,11 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
store i32 7, ptr %context, align 4
|
||||
ret void
|
||||
@@ -93,14 +93,14 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.printlock(ptr) #2
|
||||
declare void @runtime.printlock(ptr) #1
|
||||
|
||||
declare void @runtime.printint32(i32, ptr) #2
|
||||
declare void @runtime.printint32(i32, ptr) #1
|
||||
|
||||
declare void @runtime.printunlock(ptr) #2
|
||||
declare void @runtime.printunlock(ptr) #1
|
||||
|
||||
; 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 #0 {
|
||||
entry:
|
||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
||||
store i32 5, ptr %0, align 4
|
||||
@@ -126,13 +126,13 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; 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 #0 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||
@@ -146,16 +146,16 @@ declare i32 @llvm.umin.i32(i32, i32) #7
|
||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||
|
||||
; 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 #0 {
|
||||
entry:
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
||||
|
||||
; 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 #0 {
|
||||
entry:
|
||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
||||
store ptr %itf.value, ptr %0, align 4
|
||||
@@ -186,11 +186,11 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
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 #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 #3 = { 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.regularFunction" }
|
||||
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 #0 = { 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 = { "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 = { 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.regularFunction" }
|
||||
attributes #3 = { 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 = { 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 #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 #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
|
||||
+29
-29
@@ -5,30 +5,27 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
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
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, ptr) #1
|
||||
declare void @main.regularFunction(i32, ptr) #0
|
||||
|
||||
declare void @runtime.deadlock(ptr) #1
|
||||
declare void @runtime.deadlock(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||
@@ -36,23 +33,23 @@ entry:
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
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
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||
@@ -61,7 +58,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%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
|
||||
@@ -82,8 +79,11 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
store i32 7, ptr %context, align 4
|
||||
ret void
|
||||
@@ -100,14 +100,14 @@ entry:
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.printlock(ptr) #1
|
||||
declare void @runtime.printlock(ptr) #0
|
||||
|
||||
declare void @runtime.printint32(i32, ptr) #1
|
||||
declare void @runtime.printint32(i32, ptr) #0
|
||||
|
||||
declare void @runtime.printunlock(ptr) #1
|
||||
declare void @runtime.printunlock(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
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 #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
||||
@@ -135,13 +135,13 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||
@@ -155,16 +155,16 @@ declare i32 @llvm.umin.i32(i32, i32) #7
|
||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
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 #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
||||
@@ -197,11 +197,11 @@ entry:
|
||||
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 #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 #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
||||
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 #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { 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" "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #3 = { 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 = { 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 #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 #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
|
||||
Vendored
+37
-41
@@ -19,57 +19,54 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #6
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #6
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #6
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #6
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -79,12 +76,12 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
|
||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -94,12 +91,12 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -109,34 +106,33 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #6
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #6
|
||||
%1 = extractvalue %runtime._string %0, 0
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
|
||||
ret %runtime._string %0
|
||||
}
|
||||
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #7 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #6 = { nounwind }
|
||||
|
||||
Vendored
+12
-16
@@ -3,48 +3,44 @@ source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #2 {
|
||||
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret [0 x i32] zeroinitializer
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||
ret ptr %x
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||
ret ptr %x
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||
ret ptr %x
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+5
@@ -115,3 +115,8 @@ func doesNotEscapeParam(a *int, b []int, c chan int, d *[0]byte)
|
||||
//go:noescape
|
||||
func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
|
||||
}
|
||||
|
||||
//go:noheap
|
||||
func doesHeapAlloc() *int {
|
||||
return new(int)
|
||||
}
|
||||
|
||||
Vendored
+40
-30
@@ -11,95 +11,105 @@ target triple = "wasm32-unknown-wasi"
|
||||
@undefinedGlobalNotInSection = external global i32, align 4
|
||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @extern_func() #3 {
|
||||
define void @extern_func() #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @somepkg.someFunction2(ptr) #1
|
||||
declare void @somepkg.someFunction2(ptr) #0
|
||||
|
||||
; Function Attrs: inlinehint nounwind
|
||||
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #4 {
|
||||
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #5 {
|
||||
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.useGeneric(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.useGeneric(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @"main.noinlineGenericFunc[int8]"(ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #5 {
|
||||
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #4 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.functionInSection(ptr %context) unnamed_addr #5 section ".special_function_section" {
|
||||
define hidden void @main.functionInSection(ptr %context) unnamed_addr #4 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define void @exportedFunctionInSection() #6 section ".special_function_section" {
|
||||
define void @exportedFunctionInSection() #5 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.declaredImport() #7
|
||||
declare void @main.declaredImport() #6
|
||||
|
||||
declare void @imported() #8
|
||||
declare void @imported() #7
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @exported() #9 {
|
||||
define void @exported() #8 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
||||
declare void @main.undefinedFunctionNotInSection(ptr) #0
|
||||
|
||||
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #1
|
||||
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
||||
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
|
||||
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||
attributes #8 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||
attributes #9 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.doesHeapAlloc(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 4 dereferenceable(4) ptr @runtime.alloc_noheap(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #10
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #10
|
||||
ret ptr %new
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc_noheap(i32, ptr, ptr) #9
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { 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" "wasm-export-name"="extern_func" }
|
||||
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
|
||||
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||
attributes #8 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
|
||||
attributes #9 = { 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 #10 = { nounwind }
|
||||
|
||||
Vendored
+31
-31
@@ -3,31 +3,28 @@ source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %ints.len
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %ints.cap
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %ints.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
@@ -42,10 +39,10 @@ lookup.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(ptr) #1
|
||||
declare void @runtime.lookupPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
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 #1 {
|
||||
entry:
|
||||
%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
|
||||
@@ -66,10 +63,13 @@ entry:
|
||||
ret { ptr, i32, i32 } %4
|
||||
}
|
||||
|
||||
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||
|
||||
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #0
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%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
|
||||
@@ -84,7 +84,7 @@ entry:
|
||||
}
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
%copy.size = shl nuw i32 %copy.n, 2
|
||||
@@ -99,7 +99,7 @@ declare i32 @llvm.umin.i32(i32, i32) #3
|
||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #4
|
||||
|
||||
; 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 #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
@@ -118,10 +118,10 @@ slice.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.slicePanic(ptr) #1
|
||||
declare void @runtime.slicePanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
@@ -142,7 +142,7 @@ slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||
@@ -163,7 +163,7 @@ slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||
@@ -184,7 +184,7 @@ slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = getelementptr i8, ptr %p, i32 %len
|
||||
@@ -193,7 +193,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = trunc i64 %len to i32
|
||||
@@ -203,7 +203,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp ult i32 %s.len, 4
|
||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||
@@ -216,10 +216,10 @@ slicetoarray.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.sliceToArrayPointerPanic(ptr) #1
|
||||
declare void @runtime.sliceToArrayPointerPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
|
||||
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%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
|
||||
@@ -234,7 +234,7 @@ slicetoarray.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp ugt i32 %len, 1073741823
|
||||
@@ -256,10 +256,10 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp eq ptr %ptr, null
|
||||
@@ -281,7 +281,7 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
@@ -305,7 +305,7 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
@@ -328,9 +328,9 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
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 #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 #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { 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 #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
Vendored
+21
-25
@@ -7,37 +7,34 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret %runtime._string { ptr @"main$string", i32 3 }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #2 {
|
||||
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret %runtime._string zeroinitializer
|
||||
}
|
||||
|
||||
; 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 readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %s.len
|
||||
}
|
||||
|
||||
; 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 readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %s.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
@@ -48,40 +45,40 @@ lookup.next: ; preds = %entry
|
||||
ret i8 %1
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(ptr undef) #3
|
||||
call void @runtime.lookupPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(ptr) #1
|
||||
declare void @runtime.lookupPanic(ptr) #0
|
||||
|
||||
; 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 readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||
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) #2
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||
|
||||
; 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 readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||
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) #2
|
||||
%1 = xor i1 %0, true
|
||||
ret i1 %1
|
||||
}
|
||||
|
||||
; 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 readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||
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) #2
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||
|
||||
; 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 readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = zext i8 %x to i32
|
||||
%.not = icmp ugt i32 %s.len, %0
|
||||
@@ -93,11 +90,10 @@ lookup.next: ; preds = %entry
|
||||
ret i8 %2
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(ptr undef) #3
|
||||
call void @runtime.lookupPanic(ptr undef) #2
|
||||
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 #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 #3 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+24
-54
@@ -5,19 +5,16 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%main.hasPadding = type { i1, i32, i1 }
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
|
||||
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%hashmap.key = alloca %main.hasPadding, align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -27,29 +24,23 @@ entry:
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
%4 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #5
|
||||
%5 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #5
|
||||
%3 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
%6 = load i32, ptr %hashmap.value, align 4
|
||||
%4 = load i32, ptr %hashmap.value, align 4
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret i32 %6
|
||||
ret i32 %4
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
||||
|
||||
declare void @runtime.memzero(ptr, i32, ptr) #1
|
||||
|
||||
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
|
||||
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
|
||||
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%hashmap.key = alloca %main.hasPadding, align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -60,20 +51,16 @@ entry:
|
||||
store i32 5, ptr %hashmap.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
%4 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #5
|
||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #5
|
||||
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #1
|
||||
declare void @runtime.hashmapGenericSet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, ptr) #0
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
|
||||
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -84,23 +71,15 @@ entry:
|
||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||
%0 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
||||
%1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #5
|
||||
%2 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 13
|
||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #5
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 21
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
%4 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #5
|
||||
%0 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
||||
%5 = load i32, ptr %hashmap.value, align 4
|
||||
%1 = load i32, ptr %hashmap.value, align 4
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret i32 %5
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
|
||||
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -112,29 +91,20 @@ entry:
|
||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||
%0 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
||||
%1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #5
|
||||
%2 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 13
|
||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #5
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 21
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #5
|
||||
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.main(ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #5 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { noinline 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 willreturn memory(argmem: readwrite) }
|
||||
attributes #4 = { nounwind }
|
||||
|
||||
@@ -38,6 +38,7 @@ func TestErrors(t *testing.T) {
|
||||
{name: "loader-invaliddep"},
|
||||
{name: "loader-invalidpackage"},
|
||||
{name: "loader-nopackage"},
|
||||
{name: "noheap", target: "cortex-m-qemu"},
|
||||
{name: "optimizer"},
|
||||
{name: "syntax"},
|
||||
{name: "types"},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139
|
||||
@@ -18,12 +18,12 @@ require (
|
||||
go.bug.st/serial v1.6.4
|
||||
go.bytecodealliance.org v0.6.2
|
||||
go.bytecodealliance.org/cm v0.2.2
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/tools v0.30.0
|
||||
golang.org/x/net v0.50.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.org/x/tools v0.42.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/espflasher v0.6.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74
|
||||
tinygo.org/x/espflasher v0.6.1
|
||||
tinygo.org/x/go-llvm v0.0.0-20260422095634-06c6725fe5e6
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -48,6 +48,6 @@ require (
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1 // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
)
|
||||
|
||||
@@ -23,6 +23,8 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
@@ -93,24 +95,24 @@ go.bytecodealliance.org v0.6.2 h1:Jy4u5DVmSkXgsnwojBhJ+AD/YsJsR3VzVnxF0xRCqTQ=
|
||||
go.bytecodealliance.org v0.6.2/go.mod h1:gqjTJm0y9NSksG4py/lSjIQ/SNuIlOQ+hCIEPQwtJgA=
|
||||
go.bytecodealliance.org/cm v0.2.2 h1:M9iHS6qs884mbQbIjtLX1OifgyPG9DuMs2iwz8G4WQA=
|
||||
go.bytecodealliance.org/cm v0.2.2/go.mod h1:JD5vtVNZv7sBoQQkvBvAAVKJPhR/bqBH7yYXTItMfZI=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
@@ -118,7 +120,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
tinygo.org/x/espflasher v0.6.0 h1:CHbGMHAIWq1tB8FKd/QwBpNFw1jvHHxpmvZiF+QOYUo=
|
||||
tinygo.org/x/espflasher v0.6.0/go.mod h1:tr5u08HoE67WD5zxJesCiiVF/R1b6Akz3yXwh5zah8U=
|
||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 h1:ovavgTdIBWCH8YWlcfq9gkpoyT1+IxMKSn+Df27QwE8=
|
||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
tinygo.org/x/espflasher v0.6.1 h1:9jfyAP9jGjxF63FQUY2Bml9TFb5fCZYxVgbgR2IjUGs=
|
||||
tinygo.org/x/espflasher v0.6.1/go.mod h1:tr5u08HoE67WD5zxJesCiiVF/R1b6Akz3yXwh5zah8U=
|
||||
tinygo.org/x/go-llvm v0.0.0-20260422095634-06c6725fe5e6 h1:QSnqFgNV2Ij0T4hM2qKv53fcDAFElxClPjVUZXzYkWU=
|
||||
tinygo.org/x/go-llvm v0.0.0-20260422095634-06c6725fe5e6/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const version = "0.41.1"
|
||||
const version = "0.42.0-dev"
|
||||
|
||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||
// (like 0.30.0-dev-abcd012).
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ var (
|
||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||
errLoopTooLong = errors.New("interp: loop ran too many iterations")
|
||||
)
|
||||
|
||||
// This is one of the errors that can be returned from toLLVMValue when the
|
||||
@@ -29,7 +30,7 @@ var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||
err == errLoopUnrolled || err == errInvalidPtrToIntSize
|
||||
err == errLoopUnrolled || err == errLoopTooLong || err == errInvalidPtrToIntSize
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
|
||||
+33
-30
@@ -19,35 +19,38 @@ const checks = true
|
||||
|
||||
// runner contains all state related to one interp run.
|
||||
type runner struct {
|
||||
mod llvm.Module
|
||||
targetData llvm.TargetData
|
||||
builder llvm.Builder
|
||||
pointerSize uint32 // cached pointer size from the TargetData
|
||||
dataPtrType llvm.Type // often used type so created in advance
|
||||
uintptrType llvm.Type // equivalent to uintptr in Go
|
||||
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
|
||||
byteOrder binary.ByteOrder // big-endian or little-endian
|
||||
debug bool // log debug messages
|
||||
pkgName string // package name of the currently executing package
|
||||
functionCache map[llvm.Value]*function // cache of compiled functions
|
||||
objects []object // slice of objects in memory
|
||||
globals map[llvm.Value]int // map from global to index in objects slice
|
||||
start time.Time
|
||||
timeout time.Duration
|
||||
callsExecuted uint64
|
||||
mod llvm.Module
|
||||
targetData llvm.TargetData
|
||||
builder llvm.Builder
|
||||
pointerSize uint32 // cached pointer size from the TargetData
|
||||
dataPtrType llvm.Type // often used type so created in advance
|
||||
uintptrType llvm.Type // equivalent to uintptr in Go
|
||||
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
|
||||
byteOrder binary.ByteOrder // big-endian or little-endian
|
||||
debug bool // log debug messages
|
||||
pkgName string // package name of the currently executing package
|
||||
functionCache map[llvm.Value]*function // cache of compiled functions
|
||||
objects []object // slice of objects in memory
|
||||
globals map[llvm.Value]int // map from global to index in objects slice
|
||||
start time.Time
|
||||
timeout time.Duration
|
||||
maxLoopIterations int
|
||||
callsExecuted uint64
|
||||
interpErr error // set by Uint/Int when they encounter pointer data
|
||||
}
|
||||
|
||||
func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
||||
func newRunner(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bool) *runner {
|
||||
r := runner{
|
||||
mod: mod,
|
||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
byteOrder: llvmutil.ByteOrder(mod.Target()),
|
||||
debug: debug,
|
||||
functionCache: make(map[llvm.Value]*function),
|
||||
objects: []object{{}},
|
||||
globals: make(map[llvm.Value]int),
|
||||
start: time.Now(),
|
||||
timeout: timeout,
|
||||
mod: mod,
|
||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
byteOrder: llvmutil.ByteOrder(mod.Target()),
|
||||
debug: debug,
|
||||
functionCache: make(map[llvm.Value]*function),
|
||||
objects: []object{{}},
|
||||
globals: make(map[llvm.Value]int),
|
||||
start: time.Now(),
|
||||
timeout: timeout,
|
||||
maxLoopIterations: maxLoopIterations,
|
||||
}
|
||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||
r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
@@ -64,8 +67,8 @@ func (r *runner) dispose() {
|
||||
|
||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||
// Set debug to true if it should print output while running.
|
||||
func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
||||
r := newRunner(mod, timeout, debug)
|
||||
func Run(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bool) error {
|
||||
r := newRunner(mod, timeout, maxLoopIterations, debug)
|
||||
defer r.dispose()
|
||||
|
||||
initAll := mod.NamedFunction("runtime.initAll")
|
||||
@@ -204,10 +207,10 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
||||
|
||||
// RunFunc evaluates a single package initializer at compile time.
|
||||
// Set debug to true if it should print output while running.
|
||||
func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
|
||||
func RunFunc(fn llvm.Value, timeout time.Duration, maxLoopIterations int, debug bool) error {
|
||||
// Create and initialize *runner object.
|
||||
mod := fn.GlobalParent()
|
||||
r := newRunner(mod, timeout, debug)
|
||||
r := newRunner(mod, timeout, maxLoopIterations, debug)
|
||||
defer r.dispose()
|
||||
initName := fn.Name()
|
||||
if !strings.HasSuffix(initName, ".init") {
|
||||
|
||||
@@ -18,7 +18,9 @@ func TestInterp(t *testing.T) {
|
||||
"copy",
|
||||
"interface",
|
||||
"revert",
|
||||
"store",
|
||||
"alloc",
|
||||
"slicedata",
|
||||
} {
|
||||
name := name // make local to this closure
|
||||
t.Run(name, func(t *testing.T) {
|
||||
@@ -44,7 +46,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
||||
defer mod.Dispose()
|
||||
|
||||
// Perform the transform.
|
||||
err = Run(mod, 10*time.Minute, false)
|
||||
err = Run(mod, 10*time.Minute, DefaultMaxInterpBlockEntries, false)
|
||||
if err != nil {
|
||||
if err, match := err.(*Error); match {
|
||||
println(err.Error())
|
||||
|
||||
+31
-1
@@ -12,6 +12,12 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// DefaultMaxInterpBlockEntries is the default maximum number of times a single
|
||||
// basic block may be entered during interpretation of one function call. This
|
||||
// limits how far the interpreter will unroll or evaluate loops before deferring
|
||||
// the init function to runtime.
|
||||
const DefaultMaxInterpBlockEntries = 1000
|
||||
|
||||
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
|
||||
mem := memoryView{r: r, parent: parentMem}
|
||||
locals := make([]value, len(fn.locals))
|
||||
@@ -26,6 +32,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// This is used to prevent unrolling.
|
||||
var runtimeBlocks map[int]struct{}
|
||||
|
||||
// Track how many times each basic block has been entered, to detect
|
||||
// loops that are too expensive to evaluate at compile time.
|
||||
var blockCounts map[int]int
|
||||
|
||||
// Start with the first basic block and the first instruction.
|
||||
// Branch instructions may modify both bb and instIndex when branching.
|
||||
bb := fn.blocks[0]
|
||||
@@ -36,6 +46,18 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||
if instIndex == 0 {
|
||||
// This is the start of a new basic block.
|
||||
|
||||
// Check whether this block has been entered too many times,
|
||||
// which indicates an expensive loop that should be deferred
|
||||
// to runtime.
|
||||
if blockCounts == nil {
|
||||
blockCounts = make(map[int]int)
|
||||
}
|
||||
blockCounts[currentBB]++
|
||||
if r.maxLoopIterations > 0 && blockCounts[currentBB] > r.maxLoopIterations {
|
||||
return nil, mem, r.errorAt(bb.instructions[0], errLoopTooLong)
|
||||
}
|
||||
|
||||
if len(mem.instructions) != startRTInsts {
|
||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||
// This loop has been unrolled.
|
||||
@@ -277,7 +299,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// means that monotonic time in the time package is counted from
|
||||
// time.Time{}.Sub(1), which should be fine.
|
||||
locals[inst.localIndex] = literalValue{uint64(0)}
|
||||
case callFn.name == "runtime.alloc":
|
||||
case callFn.name == "runtime.alloc" || callFn.name == "runtime.alloc_noheap" || callFn.name == "runtime.alloc_zero":
|
||||
// Allocate heap memory. At compile time, this is instead done
|
||||
// by creating a global variable.
|
||||
|
||||
@@ -825,6 +847,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
}
|
||||
return nil, mem, r.errorAt(inst, errUnsupportedInst)
|
||||
}
|
||||
|
||||
// Check if an instruction triggered a recoverable error (e.g.,
|
||||
// trying to interpret pointer data as integer bytes).
|
||||
if r.interpErr != nil {
|
||||
err := r.interpErr
|
||||
r.interpErr = nil
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
}
|
||||
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
|
||||
}
|
||||
|
||||
+45
-18
@@ -299,7 +299,8 @@ func (mv *memoryView) put(index uint32, obj object) {
|
||||
}
|
||||
|
||||
// Load the value behind the given pointer. Returns nil if the pointer points to
|
||||
// an external global.
|
||||
// an external global or if the load is out of bounds of the object (in which
|
||||
// case the caller defers the load to runtime).
|
||||
func (mv *memoryView) load(p pointerValue, size uint32) value {
|
||||
if checks && mv.hasExternalStore(p) {
|
||||
panic("interp: load from object with external store")
|
||||
@@ -312,14 +313,23 @@ func (mv *memoryView) load(p pointerValue, size uint32) value {
|
||||
if p.offset() == 0 && size == obj.size {
|
||||
return obj.buffer.clone()
|
||||
}
|
||||
if checks && p.offset()+size > obj.size {
|
||||
panic("interp: load out of bounds")
|
||||
if p.offset()+size > obj.size {
|
||||
// The load is out of bounds of the object. This can happen for valid
|
||||
// Go programs, for example when dereferencing the pointer returned by
|
||||
// unsafe.SliceData on a zero-capacity slice (which points to a
|
||||
// zero-sized object). Return nil so the caller defers this load to
|
||||
// runtime instead of crashing the compiler.
|
||||
return nil
|
||||
}
|
||||
v := obj.buffer.asRawValue(mv.r)
|
||||
loadedValue := rawValue{
|
||||
buf: v.buf[p.offset() : p.offset()+size],
|
||||
loadedBuf := v.buf[p.offset() : p.offset()+size]
|
||||
if _, writable := mv.objects[p.index()]; writable {
|
||||
// This object's buffer is owned by this view, which means a later
|
||||
// store may mutate it in place (see store below). Copy the loaded
|
||||
// slice so the returned value is not aliased with the live buffer.
|
||||
loadedBuf = append([]uint64(nil), loadedBuf...)
|
||||
}
|
||||
return loadedValue
|
||||
return rawValue{buf: loadedBuf}
|
||||
}
|
||||
|
||||
// Store to the value behind the given pointer. This overwrites the value in the
|
||||
@@ -330,26 +340,37 @@ func (mv *memoryView) store(v value, p pointerValue) bool {
|
||||
if checks && mv.hasExternalLoadOrStore(p) {
|
||||
panic("interp: store to object with external load/store")
|
||||
}
|
||||
obj := mv.get(p.index())
|
||||
index := p.index()
|
||||
var obj object
|
||||
writable := false
|
||||
if mv.objects != nil {
|
||||
obj, writable = mv.objects[index]
|
||||
}
|
||||
if !writable {
|
||||
obj = mv.get(index)
|
||||
}
|
||||
if obj.buffer == nil {
|
||||
// External global, return false (for a failure).
|
||||
return false
|
||||
}
|
||||
if checks && p.offset()+v.len(mv.r) > obj.size {
|
||||
valueLen := v.len(mv.r)
|
||||
if checks && p.offset()+valueLen > obj.size {
|
||||
panic("interp: store out of bounds")
|
||||
}
|
||||
if p.offset() == 0 && v.len(mv.r) == obj.buffer.len(mv.r) {
|
||||
obj.buffer = v
|
||||
if p.offset() == 0 && valueLen == obj.buffer.len(mv.r) {
|
||||
obj.buffer = v.clone()
|
||||
} else {
|
||||
obj = obj.clone()
|
||||
if !writable {
|
||||
obj = obj.clone()
|
||||
}
|
||||
buffer := obj.buffer.asRawValue(mv.r)
|
||||
obj.buffer = buffer
|
||||
v := v.asRawValue(mv.r)
|
||||
for i := uint32(0); i < v.len(mv.r); i++ {
|
||||
for i := uint32(0); i < valueLen; i++ {
|
||||
buffer.buf[p.offset()+i] = v.buf[i]
|
||||
}
|
||||
}
|
||||
mv.put(p.index(), obj)
|
||||
mv.put(index, obj)
|
||||
return true // success
|
||||
}
|
||||
|
||||
@@ -556,11 +577,13 @@ func (v pointerValue) asRawValue(r *runner) rawValue {
|
||||
}
|
||||
|
||||
func (v pointerValue) Uint(r *runner) uint64 {
|
||||
panic("cannot convert pointer to integer")
|
||||
r.interpErr = errUnsupportedInst
|
||||
return 0
|
||||
}
|
||||
|
||||
func (v pointerValue) Int(r *runner) int64 {
|
||||
panic("cannot convert pointer to integer")
|
||||
r.interpErr = errUnsupportedInst
|
||||
return 0
|
||||
}
|
||||
|
||||
func (v pointerValue) equal(rhs pointerValue) bool {
|
||||
@@ -736,11 +759,12 @@ func (v rawValue) asRawValue(r *runner) rawValue {
|
||||
return v
|
||||
}
|
||||
|
||||
func (v rawValue) bytes() []byte {
|
||||
func (v rawValue) bytes(r *runner) []byte {
|
||||
buf := make([]byte, len(v.buf))
|
||||
for i, p := range v.buf {
|
||||
if p > 255 {
|
||||
panic("cannot convert pointer value to byte")
|
||||
r.interpErr = errUnsupportedInst
|
||||
return buf
|
||||
}
|
||||
buf[i] = byte(p)
|
||||
}
|
||||
@@ -748,7 +772,10 @@ func (v rawValue) bytes() []byte {
|
||||
}
|
||||
|
||||
func (v rawValue) Uint(r *runner) uint64 {
|
||||
buf := v.bytes()
|
||||
buf := v.bytes(r)
|
||||
if r.interpErr != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch len(v.buf) {
|
||||
case 1:
|
||||
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
; Reproduction of https://github.com/tinygo-org/tinygo/issues/4214.
|
||||
; Dereferencing the pointer returned by unsafe.SliceData on a zero-capacity
|
||||
; slice produces a load that is out of bounds of a zero-sized object. The
|
||||
; interp must defer this load to runtime instead of crashing the compiler.
|
||||
|
||||
@main.zeroSized = global {} zeroinitializer
|
||||
@main.v = global i64 0
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @main.init(ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init(ptr %context) unnamed_addr {
|
||||
entry:
|
||||
%val = load i64, ptr @main.zeroSized
|
||||
store i64 %val, ptr @main.v
|
||||
ret void
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@main.zeroSized = local_unnamed_addr global {} zeroinitializer
|
||||
@main.v = local_unnamed_addr global i64 0
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
%val = load i64, ptr @main.zeroSized, align 8
|
||||
store i64 %val, ptr @main.v, align 8
|
||||
ret void
|
||||
}
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@overlap.buf = global [4 x i8] c"\01\02\03\04"
|
||||
@alias.src = global [4 x i8] c"\05\06\07\08"
|
||||
@alias.dst = global [2 x i8] zeroinitializer
|
||||
@reload.buf = global [4 x i8] c"\01\02\03\04"
|
||||
@reload.out = global [2 x i8] zeroinitializer
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @overlap.init(ptr undef)
|
||||
call void @alias.init(ptr undef)
|
||||
call void @reload.init(ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @overlap.init(ptr %context) unnamed_addr {
|
||||
entry:
|
||||
%tail = getelementptr [4 x i8], ptr @overlap.buf, i32 0, i32 3
|
||||
store i8 9, ptr %tail
|
||||
%val = load i16, ptr @overlap.buf
|
||||
%dst = getelementptr [4 x i8], ptr @overlap.buf, i32 0, i32 1
|
||||
store i16 %val, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @alias.init(ptr %context) unnamed_addr {
|
||||
entry:
|
||||
%src = getelementptr [4 x i8], ptr @alias.src, i32 0, i32 1
|
||||
%val = load i16, ptr %src
|
||||
store i16 %val, ptr @alias.dst
|
||||
store i8 9, ptr @alias.dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @reload.init(ptr %context) unnamed_addr {
|
||||
entry:
|
||||
; First store makes reload.buf writable in the current memory view.
|
||||
%tail = getelementptr [4 x i8], ptr @reload.buf, i32 0, i32 3
|
||||
store i8 9, ptr %tail
|
||||
; Partial load whose result may share the underlying buffer.
|
||||
%val = load i16, ptr @reload.buf
|
||||
; Subsequent in-place partial store; this must not corrupt %val.
|
||||
store i8 99, ptr @reload.buf
|
||||
; Write the originally-loaded value to a separate global.
|
||||
store i16 %val, ptr @reload.out
|
||||
ret void
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@overlap.buf = local_unnamed_addr global [4 x i8] c"\01\01\02\09"
|
||||
@alias.src = local_unnamed_addr global [4 x i8] c"\05\06\07\08"
|
||||
@alias.dst = local_unnamed_addr global [2 x i8] c"\09\07"
|
||||
@reload.buf = local_unnamed_addr global [4 x i8] c"c\02\03\09"
|
||||
@reload.out = local_unnamed_addr global [2 x i8] c"\01\02"
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
+2
-1
@@ -245,7 +245,8 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
"internal/cm/": false,
|
||||
"internal/futex/": false,
|
||||
"internal/fuzz/": false,
|
||||
"internal/itoa": false,
|
||||
"internal/itoa/": false,
|
||||
"internal/poll/": false,
|
||||
"internal/reflectlite/": false,
|
||||
"internal/gclayout": false,
|
||||
"internal/task/": false,
|
||||
|
||||
+25
-4
@@ -27,8 +27,6 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var initFileVersions = func(info *types.Info) {}
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
config *compileopts.Config
|
||||
@@ -304,6 +302,29 @@ func (p *Program) Sorted() []*Package {
|
||||
return p.sorted
|
||||
}
|
||||
|
||||
// StripVarInitializer removes the package-level initializer for the named
|
||||
// variable from the type info. This prevents go/ssa from emitting an init
|
||||
// store for it, leaving the global zero-initialized in the IR. An external
|
||||
// value (e.g. from -ldflags -X via makeGlobalsModule) can then be linked in
|
||||
// at final link time without any runtime init overwriting it.
|
||||
//
|
||||
// Must be called after Parse() (typechecking populates InitOrder) and before
|
||||
// LoadSSA() (which consumes InitOrder to build the package init function).
|
||||
//
|
||||
// Only 1:1 var initializers (var x = expr) are matched. Multi-variable
|
||||
// initializers (var x, y = f()) are left untouched.
|
||||
func (p *Package) StripVarInitializer(name string) {
|
||||
n := 0
|
||||
for _, init := range p.info.InitOrder {
|
||||
if len(init.Lhs) == 1 && init.Lhs[0].Name() == name {
|
||||
continue // drop this initializer
|
||||
}
|
||||
p.info.InitOrder[n] = init
|
||||
n++
|
||||
}
|
||||
p.info.InitOrder = p.info.InitOrder[:n]
|
||||
}
|
||||
|
||||
// MainPkg returns the last package in the Sorted() slice. This is the main
|
||||
// package of the program.
|
||||
func (p *Program) MainPkg() *Package {
|
||||
@@ -412,7 +433,7 @@ func (p *Package) Check() error {
|
||||
}
|
||||
checker.GoVersion = fmt.Sprintf("go%d.%d", major, minor)
|
||||
}
|
||||
initFileVersions(&p.info)
|
||||
p.info.FileVersions = make(map[*ast.File]string)
|
||||
|
||||
// Do typechecking of the package.
|
||||
packageName := p.ImportPath
|
||||
@@ -462,7 +483,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
if !filepath.IsAbs(file) {
|
||||
file = filepath.Join(p.Dir, file)
|
||||
}
|
||||
f, err := p.parseFile(file, parser.ParseComments)
|
||||
f, err := p.parseFile(file, parser.ParseComments|parser.SkipObjectResolution)
|
||||
if err != nil {
|
||||
fileErrs = append(fileErrs, err)
|
||||
return
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
//go:build go1.22
|
||||
|
||||
// types.Info.FileVersions was added in Go 1.22, so we can only initialize it
|
||||
// when built with Go 1.22.
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initFileVersions = func(info *types.Info) {
|
||||
info.FileVersions = make(map[*ast.File]string)
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/diagnostics"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"tinygo.org/x/espflasher/pkg/espflasher"
|
||||
@@ -208,6 +209,8 @@ func Build(pkgName, outpath string, config *compileopts.Config) error {
|
||||
// Test runs the tests in the given package. Returns whether the test passed and
|
||||
// possibly an error if the test failed to run.
|
||||
func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options, outpath string) (bool, error) {
|
||||
optionsCopy := *options
|
||||
options = &optionsCopy
|
||||
options.TestConfig.CompileTestBinary = true
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
@@ -394,7 +397,7 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
||||
fileExt = filepath.Ext(config.Target.FlashFilename)
|
||||
case "openocd":
|
||||
fileExt = ".hex"
|
||||
case "bmp":
|
||||
case "bmp", "probe-rs":
|
||||
fileExt = ".elf"
|
||||
case "adb":
|
||||
fileExt = ".hex"
|
||||
@@ -534,6 +537,16 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
case "probe-rs":
|
||||
// TODO: this halts the target after flashing.
|
||||
// See: https://github.com/probe-rs/probe-rs/discussions/4005
|
||||
cmd := executeCommand(config.Options, "probe-rs", "download", "--chip="+config.Target.ProbeRSChip, "--verify", result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
case "adb":
|
||||
// Run pre-flash adb shell commands.
|
||||
for _, preCmd := range config.Target.ADBPreCommands {
|
||||
@@ -588,7 +601,7 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||
}
|
||||
if options.Monitor {
|
||||
return Monitor(result.Executable, "", config)
|
||||
return Monitor(result.Executable, port, config)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -696,6 +709,21 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||
daemon.Stdout = w
|
||||
daemon.Stderr = w
|
||||
}
|
||||
case "probe-rs":
|
||||
port = ":1337"
|
||||
gdbCommands = append(gdbCommands, "monitor halt", "load", "monitor reset halt")
|
||||
|
||||
daemon = executeCommand(config.Options, "probe-rs", "gdb", "--chip="+config.Target.ProbeRSChip)
|
||||
if ocdOutput {
|
||||
// Make it clear which output is from the daemon.
|
||||
w := &ColorWriter{
|
||||
Out: colorable.NewColorableStderr(),
|
||||
Prefix: "probe-rs: ",
|
||||
Color: TermColorYellow,
|
||||
}
|
||||
daemon.Stdout = w
|
||||
daemon.Stderr = w
|
||||
}
|
||||
case "jlink":
|
||||
port = ":2331"
|
||||
gdbCommands = append(gdbCommands, "load", "monitor reset halt")
|
||||
@@ -1738,6 +1766,7 @@ func main() {
|
||||
serial := flag.String("serial", "", "which serial output to use (none, uart, usb, rtt)")
|
||||
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
||||
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
||||
interpLoopLimit := flag.Int("interp-loop-limit", interp.DefaultMaxInterpBlockEntries, "maximum loop iterations during interp (0 to disable)")
|
||||
var tags buildutil.TagsFlag
|
||||
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
|
||||
target := flag.String("target", "", "chip/board name or JSON target specification file")
|
||||
@@ -1751,6 +1780,7 @@ func main() {
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full, html)")
|
||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
||||
printAllocsCoverString := flag.String("print-allocs-cover", "", "like -print-allocs, but in go coverage tool format")
|
||||
printCommands := flag.Bool("x", false, "Print commands")
|
||||
flagJSON := flag.Bool("json", false, "print output in JSON format")
|
||||
parallelism := flag.Int("p", runtime.GOMAXPROCS(0), "the number of build jobs that can run in parallel")
|
||||
@@ -1831,8 +1861,14 @@ func main() {
|
||||
}
|
||||
|
||||
var printAllocs *regexp.Regexp
|
||||
if *printAllocsString != "" {
|
||||
printAllocs, err = regexp.Compile(*printAllocsString)
|
||||
printAllocsCover := false
|
||||
printAllocsPattern := *printAllocsString
|
||||
if *printAllocsCoverString != "" {
|
||||
printAllocsPattern = *printAllocsCoverString
|
||||
printAllocsCover = true
|
||||
}
|
||||
if printAllocsPattern != "" {
|
||||
printAllocs, err = regexp.Compile(printAllocsPattern)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
@@ -1855,42 +1891,44 @@ func main() {
|
||||
}
|
||||
|
||||
options := &compileopts.Options{
|
||||
GOOS: goenv.Get("GOOS"),
|
||||
GOARCH: goenv.Get("GOARCH"),
|
||||
GOARM: goenv.Get("GOARM"),
|
||||
GOMIPS: goenv.Get("GOMIPS"),
|
||||
Target: *target,
|
||||
BuildMode: *buildMode,
|
||||
StackSize: stackSize,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
Serial: *serial,
|
||||
Work: *work,
|
||||
InterpTimeout: *interpTimeout,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
SkipDWARF: *skipDwarf,
|
||||
Semaphore: make(chan struct{}, *parallelism),
|
||||
Debug: !*nodebug,
|
||||
Nobounds: *nobounds,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
Tags: []string(tags),
|
||||
TestConfig: testConfig,
|
||||
GlobalValues: globalVarValues,
|
||||
Programmer: *programmer,
|
||||
OpenOCDCommands: ocdCommands,
|
||||
LLVMFeatures: *llvmFeatures,
|
||||
Monitor: *monitor,
|
||||
BaudRate: *baudrate,
|
||||
Timeout: *timeout,
|
||||
WITPackage: witPackage,
|
||||
WITWorld: witWorld,
|
||||
GoCompatibility: *gocompatibility,
|
||||
GOOS: goenv.Get("GOOS"),
|
||||
GOARCH: goenv.Get("GOARCH"),
|
||||
GOARM: goenv.Get("GOARM"),
|
||||
GOMIPS: goenv.Get("GOMIPS"),
|
||||
Target: *target,
|
||||
BuildMode: *buildMode,
|
||||
StackSize: stackSize,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
Serial: *serial,
|
||||
Work: *work,
|
||||
InterpTimeout: *interpTimeout,
|
||||
InterpMaxLoopIterations: *interpLoopLimit,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
SkipDWARF: *skipDwarf,
|
||||
Semaphore: make(chan struct{}, *parallelism),
|
||||
Debug: !*nodebug,
|
||||
Nobounds: *nobounds,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
PrintAllocsCover: printAllocsCover,
|
||||
Tags: []string(tags),
|
||||
TestConfig: testConfig,
|
||||
GlobalValues: globalVarValues,
|
||||
Programmer: *programmer,
|
||||
OpenOCDCommands: ocdCommands,
|
||||
LLVMFeatures: *llvmFeatures,
|
||||
Monitor: *monitor,
|
||||
BaudRate: *baudrate,
|
||||
Timeout: *timeout,
|
||||
WITPackage: witPackage,
|
||||
WITWorld: witWorld,
|
||||
GoCompatibility: *gocompatibility,
|
||||
}
|
||||
if *printCommands {
|
||||
options.PrintCommands = printCommand
|
||||
|
||||
+21
-9
@@ -46,15 +46,6 @@ var supportedLinuxArches = map[string]string{
|
||||
"WASIp1": "wasip1/wasm",
|
||||
}
|
||||
|
||||
func init() {
|
||||
major, _, _ := goenv.GetGorootVersion()
|
||||
if major < 21 {
|
||||
// Go 1.20 backwards compatibility.
|
||||
// Should be removed once we drop support for Go 1.20.
|
||||
delete(supportedLinuxArches, "WASIp1")
|
||||
}
|
||||
}
|
||||
|
||||
var sema = make(chan struct{}, runtime.NumCPU())
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
@@ -76,7 +67,10 @@ func TestBuild(t *testing.T) {
|
||||
"init_multi.go",
|
||||
"interface.go",
|
||||
"json.go",
|
||||
"localtypes/",
|
||||
"localtypes.go",
|
||||
"map.go",
|
||||
"map_bigkey.go",
|
||||
"math.go",
|
||||
"oldgo/",
|
||||
"print.go",
|
||||
@@ -155,6 +149,19 @@ func TestBuild(t *testing.T) {
|
||||
}
|
||||
runTestWithConfig("ldflags.go", t, opts, nil, nil)
|
||||
})
|
||||
|
||||
// Same as ldflags, but the global has a default value in source.
|
||||
// -ldflags -X must override it, matching standard Go behaviour.
|
||||
t.Run("ldflags-initialized", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := optionsFromTarget("", sema)
|
||||
opts.GlobalValues = map[string]map[string]string{
|
||||
"main": {
|
||||
"someGlobal": "foobar",
|
||||
},
|
||||
}
|
||||
runTestWithConfig("ldflags-initialized.go", t, opts, nil, nil)
|
||||
})
|
||||
})
|
||||
|
||||
if testing.Short() {
|
||||
@@ -283,6 +290,11 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
// limited amount of memory.
|
||||
continue
|
||||
|
||||
case "map_bigkey.go":
|
||||
// Compiler generates many large stack temporaries for [256]byte
|
||||
// map keys, overflowing the goroutine stack (384 bytes).
|
||||
continue
|
||||
|
||||
case "gc.go":
|
||||
// Does not pass due to high mark false positive rate.
|
||||
continue
|
||||
|
||||
+1
-1
@@ -278,7 +278,7 @@ func ListSerialPorts() ([]SerialPortInfo, error) {
|
||||
return serialPortInfo, nil
|
||||
}
|
||||
|
||||
var addressMatch = regexp.MustCompile(`^panic: runtime error at 0x([0-9a-f]+): `)
|
||||
var addressMatch = regexp.MustCompile(`panic: runtime error at 0x([0-9a-f]+): `)
|
||||
|
||||
// Extract the address from the "panic: runtime error at" message.
|
||||
func extractPanicAddress(line []byte) uint64 {
|
||||
|
||||
@@ -20,7 +20,7 @@ type reader struct {
|
||||
|
||||
func (r *reader) Read(b []byte) (n int, err error) {
|
||||
if len(b) != 0 {
|
||||
libc_arc4random_buf(unsafe.Pointer(&b[0]), uint(len(b)))
|
||||
libc_arc4random_buf(unsafe.Pointer(unsafe.SliceData(b)), uint(len(b)))
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func (r *reader) Read(b []byte) (n int, err error) {
|
||||
// See for example: https://github.com/golang/go/issues/33542
|
||||
// For Windows 7 and newer, we might switch to ProcessPrng in the future
|
||||
// (which is a documented function and might be a tiny bit faster).
|
||||
ok := libc_RtlGenRandom(unsafe.Pointer(&b[0]), len(b))
|
||||
ok := libc_RtlGenRandom(unsafe.Pointer(unsafe.SliceData(b)), len(b))
|
||||
if !ok {
|
||||
return 0, errRandom
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// This is a very minimal bootloader for the ESP32-C6. It only initializes the
|
||||
// flash and then continues with the generic RISC-V initialization code, which
|
||||
// in turn will call runtime.main.
|
||||
// It is written in assembly (and not in a higher level language) to make sure
|
||||
// it is entirely loaded into IRAM and doesn't accidentally call functions
|
||||
// stored in IROM.
|
||||
//
|
||||
// The ESP32-C6 has a unified IRAM/DRAM address space at 0x40800000, and
|
||||
// separate DROM (0x42800000) / IROM (0x42000000) flash-mapped regions.
|
||||
|
||||
.section .init
|
||||
.global call_start_cpu0
|
||||
.type call_start_cpu0,@function
|
||||
call_start_cpu0:
|
||||
// At this point:
|
||||
// - The ROM bootloader is finished and has jumped to here.
|
||||
// - We're running from IRAM: both IRAM and DRAM segments have been loaded
|
||||
// by the ROM bootloader.
|
||||
// - We have a usable stack (but not the one we would like to use).
|
||||
// - No flash mappings (MMU) are set up yet.
|
||||
|
||||
// Reset MMU, see bootloader_reset_mmu in the ESP-IDF.
|
||||
call Cache_Suspend_ICache
|
||||
mv s0, a0 // autoload value
|
||||
call Cache_Invalidate_ICache_All
|
||||
call Cache_MMU_Init
|
||||
|
||||
// Set up flash mapping (both IROM and DROM).
|
||||
// On ESP32-C6, Cache_Dbus_MMU_Set is replaced by Cache_MSPI_MMU_Set
|
||||
// which has an extra "sensitive" parameter.
|
||||
// C equivalent:
|
||||
// Cache_MSPI_MMU_Set(0, 0, 0x42000000, 0, 64, 256, 0)
|
||||
// Maps 16MB starting at 0x42000000, covering both IROM and DROM.
|
||||
li a0, 0 // sensitive: no flash encryption
|
||||
li a1, 0 // ext_ram: MMU_ACCESS_FLASH
|
||||
li a2, 0x42000000 // vaddr: start of flash-mapped region
|
||||
li a3, 0 // paddr: physical address in the flash chip
|
||||
li a4, 64 // psize: always 64 (kilobytes)
|
||||
li a5, 256 // num: pages (16MB / 64K = 256, covers IROM+DROM)
|
||||
li a6, 0 // fixed
|
||||
call Cache_MSPI_MMU_Set
|
||||
|
||||
// Enable the flash cache.
|
||||
mv a0, s0 // restore autoload value from Cache_Suspend_ICache call
|
||||
call Cache_Resume_ICache
|
||||
|
||||
// Jump to generic RISC-V initialization, which initializes the stack
|
||||
// pointer and globals register. It should not return.
|
||||
j _start
|
||||
|
||||
.section .text.exception_vectors
|
||||
.global _vector_table
|
||||
.type _vector_table,@function
|
||||
|
||||
_vector_table:
|
||||
|
||||
.option push
|
||||
.option norvc
|
||||
|
||||
.rept 32
|
||||
j handleInterruptASM /* interrupt handler */
|
||||
.endr
|
||||
|
||||
.option pop
|
||||
|
||||
.size _vector_table, .-_vector_table
|
||||
@@ -85,6 +85,9 @@ var (
|
||||
SIO = (*SIO_Type)(unsafe.Add(unsafe.Pointer(REG_BASE), uintptr(0x0134)))
|
||||
|
||||
INTERRUPT = (*INTERRUPT_Type)(unsafe.Add(unsafe.Pointer(REG_BASE), uintptr(0x0200)))
|
||||
|
||||
// BIOS interrupt flags
|
||||
BIOS_IF = (*volatile.Register16)(unsafe.Pointer(uintptr(0x03007FF8)))
|
||||
)
|
||||
|
||||
// Main memory sections
|
||||
@@ -825,3 +828,60 @@ const (
|
||||
OAMOBJ_ATT2_PB_Pos = 0xC
|
||||
OAMOBJ_ATT2_PB_Msk = 0xF
|
||||
)
|
||||
|
||||
// Constants for SOUND: sound control
|
||||
const (
|
||||
SSW_INC = 0x0 // Increasing sweep rate
|
||||
SSW_DEC = 0x0008 // Decreasing sweep rate
|
||||
SSW_OFF = 0x0008 // Disable sweep altogether
|
||||
|
||||
SSQR_DUTY1_8 = 0x0 // 12.5% duty cycle (#-------)
|
||||
SSQR_DUTY1_4 = 0x0040 // 25% duty cycle (##------)
|
||||
SSQR_DUTY1_2 = 0x0080 // 50% duty cycle (####----)
|
||||
SSQR_DUTY3_4 = 0x00C0 // 75% duty cycle (######--) Equivalent to 25%
|
||||
SSQR_INC = 0x0 // Increasing volume
|
||||
SSQR_DEC = 0x0800 // Decreasing volume
|
||||
|
||||
SFREQ_HOLD = 0x0 // Continuous play
|
||||
SFREQ_TIMED = 0x4000 // Timed play
|
||||
SFREQ_RESET = 0x8000 // Reset sound
|
||||
|
||||
SDMG_SQR1 = 0x01
|
||||
SDMG_SQR2 = 0x02
|
||||
SDMG_WAVE = 0x04
|
||||
SDMG_NOISE = 0x08
|
||||
|
||||
SDMG_LSQR1 = 0x0100 // Enable channel 1 on left
|
||||
SDMG_LSQR2 = 0x0200 // Enable channel 2 on left
|
||||
SDMG_LWAVE = 0x0400 // Enable channel 3 on left
|
||||
SDMG_LNOISE = 0x0800 // Enable channel 4 on left
|
||||
SDMG_RSQR1 = 0x1000 // Enable channel 1 on right
|
||||
SDMG_RSQR2 = 0x2000 // Enable channel 2 on right
|
||||
SDMG_RWAVE = 0x4000 // Enable channel 3 on right
|
||||
SDMG_RNOISE = 0x8000 // Enable channel 4 on right
|
||||
|
||||
SDS_DMG25 = 0x0 // Tone generators at 25% volume
|
||||
SDS_DMG50 = 0x0001 // Tone generators at 50% volume
|
||||
SDS_DMG100 = 0x0002 // Tone generators at 100% volume
|
||||
SDS_A50 = 0x0 // Direct Sound A at 50% volume
|
||||
SDS_A100 = 0x0004 // Direct Sound A at 100% volume
|
||||
SDS_B50 = 0x0 // Direct Sound B at 50% volume
|
||||
SDS_B100 = 0x0008 // Direct Sound B at 100% volume
|
||||
SDS_AR = 0x0100 // Enable Direct Sound A on right
|
||||
SDS_AL = 0x0200 // Enable Direct Sound A on left
|
||||
SDS_ATMR0 = 0x0 // Direct Sound A to use timer 0
|
||||
SDS_ATMR1 = 0x0400 // Direct Sound A to use timer 1
|
||||
SDS_ARESET = 0x0800 // Reset FIFO of Direct Sound A
|
||||
SDS_BR = 0x1000 // Enable Direct Sound B on right
|
||||
SDS_BL = 0x2000 // Enable Direct Sound B on left
|
||||
SDS_BTMR0 = 0x0 // Direct Sound B to use timer 0
|
||||
SDS_BTMR1 = 0x4000 // Direct Sound B to use timer 1
|
||||
SDS_BRESET = 0x8000 // Reset FIFO of Direct Sound B
|
||||
|
||||
SSTAT_SQR1 = 0x0001 // (R) Channel 1 status
|
||||
SSTAT_SQR2 = 0x0002 // (R) Channel 2 status
|
||||
SSTAT_WAVE = 0x0004 // (R) Channel 3 status
|
||||
SSTAT_NOISE = 0x0008 // (R) Channel 4 status
|
||||
SSTAT_DISABLE = 0 // Disable sound
|
||||
SSTAT_ENABLE = 0x0080 // Enable sound. NOTE: enable before using any other sound regs
|
||||
)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build go1.23
|
||||
|
||||
package cm
|
||||
|
||||
import "structs"
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build !go1.23
|
||||
|
||||
package cm
|
||||
|
||||
// HostLayout marks a struct as using host memory layout.
|
||||
// See [structs.HostLayout] in Go 1.23 or later.
|
||||
type HostLayout struct {
|
||||
_ hostLayout // prevent accidental conversion with plain struct{}
|
||||
}
|
||||
|
||||
type hostLayout struct{}
|
||||
@@ -0,0 +1,45 @@
|
||||
//go:build wasip1
|
||||
|
||||
// Internal-test helpers exposed via go:linkname so user code can drive
|
||||
// the deadline-aware Read/Write loop without becoming a stdlib package.
|
||||
// Not part of the public API; the names are intentionally awkward to
|
||||
// signal "for tests only".
|
||||
|
||||
package poll
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// pollTestReadWithDeadline opens a pollable FD wrapper for sysfd, sets
|
||||
// a read deadline d into the future, calls Read once, and returns
|
||||
// (n, err). Caller is responsible for closing sysfd.
|
||||
//
|
||||
//go:linkname pollTestReadWithDeadline
|
||||
func pollTestReadWithDeadline(sysfd int, d time.Duration, p []byte) (int, error) {
|
||||
fd := &FD{Sysfd: sysfd, IsStream: true}
|
||||
// Best-effort init; ignore error so a caller using a not-fcntl-able FD
|
||||
// (stdin under wazero, etc.) still gets to test the deadline path on
|
||||
// whatever park behaviour the runtime gives.
|
||||
_ = fd.Init("test", true)
|
||||
if err := fd.SetReadDeadline(time.Now().Add(d)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return fd.Read(p)
|
||||
}
|
||||
|
||||
// pollTestSetNonblock toggles O_NONBLOCK on a raw sysfd. Useful in
|
||||
// tests when the caller wants to ensure the FD is in nonblocking mode
|
||||
// before calling pollTestReadWithDeadline (Init is best-effort and may
|
||||
// silently skip).
|
||||
//
|
||||
//go:linkname pollTestSetNonblock
|
||||
func pollTestSetNonblock(sysfd int) error {
|
||||
flags, err := syscall.Fcntl(sysfd, syscall.F_GETFL, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = syscall.Fcntl(sysfd, syscall.F_SETFL, flags|syscall.O_NONBLOCK)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
//go:build wasip1
|
||||
|
||||
// Package poll is a minimal subset of upstream Go's internal/poll, scoped
|
||||
// to what is needed to back a wasip1 net implementation on top of
|
||||
// TinyGo's cooperative-scheduler netpoll integration.
|
||||
//
|
||||
// On wasip1 the cooperative scheduler integrates poll_oneoff with FD
|
||||
// waiters (see runtime/netpoll_wasip1.go and syscall/syscall_libc_wasip1.go).
|
||||
// This package wraps the syscall layer to:
|
||||
//
|
||||
// - own the O_NONBLOCK policy decision (set on Init for pollable FDs),
|
||||
// unblocking the EAGAIN→park retry loop that syscall.Read already has;
|
||||
// - provide a Go-shaped FD type that net.* can use without reaching
|
||||
// into syscall directly;
|
||||
// - thread per-FD read/write deadlines through a runtime helper that
|
||||
// lets a time.AfterFunc callback wake the parked goroutine;
|
||||
// - dispatch socket FDs through wasi sock_recv / sock_send / sock_accept
|
||||
// / sock_shutdown so net.Conn/net.Listener (via upstream Go's
|
||||
// net/file_wasip1.go) work end-to-end.
|
||||
package poll
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"internal/task"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ErrFileClosing is returned when a Read or Write is started on a closed FD.
|
||||
var ErrFileClosing = errors.New("use of closed file")
|
||||
|
||||
// ErrNetClosing is returned for network operations on a closed FD.
|
||||
var ErrNetClosing = errors.New("use of closed network connection")
|
||||
|
||||
// ErrDeadlineExceeded is returned by Read/Write when a deadline expired.
|
||||
// Matches the error returned by os.IsTimeout-style helpers.
|
||||
var ErrDeadlineExceeded = errors.New("i/o timeout")
|
||||
|
||||
// ErrNoDeadline is returned if SetDeadline is called on an FD whose
|
||||
// underlying type does not support deadlines.
|
||||
var ErrNoDeadline = errors.New("file type does not support deadline")
|
||||
|
||||
// pollMode constants must mirror runtime/netpoll_wasip1.go's pollRead/
|
||||
// pollWrite values.
|
||||
const (
|
||||
pollModeRead uint8 = 1
|
||||
pollModeWrite uint8 = 2
|
||||
)
|
||||
|
||||
//go:linkname runtime_netpoll_addwait runtime.runtime_netpoll_addwait
|
||||
func runtime_netpoll_addwait(fd uint32, mode uint8) uintptr
|
||||
|
||||
//go:linkname runtime_netpoll_done runtime.runtime_netpoll_done
|
||||
func runtime_netpoll_done(pd uintptr)
|
||||
|
||||
//go:linkname runtime_netpoll_pdfired runtime.runtime_netpoll_pdfired
|
||||
func runtime_netpoll_pdfired(pd uintptr) bool
|
||||
|
||||
//go:linkname runtime_netpoll_wake runtime.runtime_netpoll_wake
|
||||
func runtime_netpoll_wake(pd uintptr)
|
||||
|
||||
//go:linkname fd_fdstat_get_type syscall.fd_fdstat_get_type
|
||||
func fd_fdstat_get_type(fd int) (syscall.Filetype, error)
|
||||
|
||||
// wasiIovec / wasiCiovec mirror wasi-snapshot-preview1's iovec / ciovec
|
||||
// records: a buffer pointer plus a 32-bit length. Marshalled inline with
|
||||
// each fd_read / fd_write / sock_recv / sock_send call.
|
||||
type wasiIovec struct {
|
||||
buf *byte
|
||||
bufLen uint32
|
||||
}
|
||||
|
||||
// fd_read / fd_write are bound directly here rather than going through
|
||||
// wasi-libc so the deadline-aware Read/Write loop can observe EAGAIN
|
||||
// cleanly. Outside package runtime, TinyGo's wasmimport binding
|
||||
// restricts us to unsafe.Pointer for struct args and uint32 for the
|
||||
// errno result; the wasi spec uses *iovec and uint16 respectively but
|
||||
// the wire layout is identical.
|
||||
//
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_read
|
||||
func wasi_fd_read(fd int32, iovs unsafe.Pointer, iovsLen uint32, nread unsafe.Pointer) uint32
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_write
|
||||
func wasi_fd_write(fd int32, iovs unsafe.Pointer, iovsLen uint32, nwritten unsafe.Pointer) uint32
|
||||
|
||||
// sock_recv / sock_send are the socket-data wasi syscalls used when the
|
||||
// FD's Filetype indicates a socket. We bind them here for the same
|
||||
// reason as fd_read / fd_write: the deadline-aware loop wants direct
|
||||
// access to the EAGAIN signal without libc's translation layer.
|
||||
//
|
||||
//go:wasmimport wasi_snapshot_preview1 sock_recv
|
||||
func wasi_sock_recv(fd int32, riData unsafe.Pointer, riDataLen uint32, riFlags uint32, roDatalen unsafe.Pointer, roFlags unsafe.Pointer) uint32
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 sock_send
|
||||
func wasi_sock_send(fd int32, siData unsafe.Pointer, siDataLen uint32, siFlags uint32, soDatalen unsafe.Pointer) uint32
|
||||
|
||||
// SysFile carries per-FD bookkeeping that upstream Go's poll.FD uses to
|
||||
// share an underlying syscall FD between an os.File and a net.Conn (see
|
||||
// Copy below). RefCountPtr / RefCount handle the shared-ownership case;
|
||||
// Filetype caches the wasi filetype so socket-vs-file dispatch in Read /
|
||||
// Write is a single integer compare on the hot path.
|
||||
//
|
||||
// wasip1 is single-threaded, so the refcount is a plain int — no atomics
|
||||
// needed. Match upstream's field naming for source-level compatibility
|
||||
// with code that constructs FDs via struct literal (e.g. upstream's
|
||||
// net/fd_fake.go).
|
||||
type SysFile struct {
|
||||
RefCountPtr *int32
|
||||
RefCount int32
|
||||
Filetype uint32
|
||||
}
|
||||
|
||||
// init lazily allocates the refcount the first time it's needed (i.e.
|
||||
// the first Init or Copy on this FD). A zero SysFile starts at refcount
|
||||
// 1 — the FD's sole owner is the caller.
|
||||
func (s *SysFile) init() {
|
||||
if s.RefCountPtr == nil {
|
||||
s.RefCount = 1
|
||||
s.RefCountPtr = &s.RefCount
|
||||
}
|
||||
}
|
||||
|
||||
// ref increments the shared refcount and returns a SysFile that points
|
||||
// at the same counter. Used by FD.Copy.
|
||||
func (s *SysFile) ref() SysFile {
|
||||
s.init()
|
||||
*s.RefCountPtr++
|
||||
return SysFile{RefCountPtr: s.RefCountPtr, Filetype: s.Filetype}
|
||||
}
|
||||
|
||||
// destroy decrements the refcount and reports whether the underlying
|
||||
// syscall FD should now be closed (i.e. this was the last owner).
|
||||
func (s *SysFile) destroy() bool {
|
||||
if s.RefCountPtr == nil {
|
||||
return true
|
||||
}
|
||||
*s.RefCountPtr--
|
||||
return *s.RefCountPtr <= 0
|
||||
}
|
||||
|
||||
// FD is the wasip1 file/socket descriptor wrapped with the bookkeeping
|
||||
// that net and os rely on. It owns the lifecycle of the underlying
|
||||
// syscall FD (modulo the Copy / refcount handoff between os.File and
|
||||
// net.Conn).
|
||||
//
|
||||
// The struct mirrors upstream Go's internal/poll.FD field naming
|
||||
// (Sysfd, IsStream, ZeroReadIsEOF, SysFile) so that upstream's
|
||||
// net/file_wasip1.go and net/fd_fake.go can construct one via struct
|
||||
// literal without modification.
|
||||
type FD struct {
|
||||
Sysfd int
|
||||
SysFile SysFile
|
||||
IsStream bool
|
||||
ZeroReadIsEOF bool
|
||||
closed bool
|
||||
|
||||
// Per-FD deadlines, zero means "no deadline". Subsequent Read/Write
|
||||
// calls observe whatever the current value is at call time; an
|
||||
// in-flight call uses the deadline it captured at its start.
|
||||
rDeadline time.Time
|
||||
wDeadline time.Time
|
||||
}
|
||||
|
||||
// Init readies the FD for use. When pollable is true (i.e. the FD might
|
||||
// block — sockets, pipes, FIFOs), Init sets O_NONBLOCK so that
|
||||
// Read/Write enter the EAGAIN→park retry loop instead of blocking the
|
||||
// entire wasm module.
|
||||
//
|
||||
// Init also caches the wasi filetype so the Read/Write hot path can
|
||||
// dispatch socket vs file with a single integer compare.
|
||||
//
|
||||
// The net argument is currently ignored but kept for parity with
|
||||
// upstream Go.
|
||||
func (fd *FD) Init(net string, pollable bool) error {
|
||||
_ = net
|
||||
fd.SysFile.init()
|
||||
if ft, err := fd_fdstat_get_type(fd.Sysfd); err == nil {
|
||||
fd.SysFile.Filetype = uint32(ft)
|
||||
}
|
||||
if !pollable {
|
||||
return nil
|
||||
}
|
||||
flags, err := syscall.Fcntl(fd.Sysfd, syscall.F_GETFL, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flags&syscall.O_NONBLOCK != 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err := syscall.Fcntl(fd.Sysfd, syscall.F_SETFL, flags|syscall.O_NONBLOCK); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a duplicate FD that shares the underlying Sysfd through
|
||||
// the SysFile refcount. The original and the copy can independently
|
||||
// call Close — only the last one actually issues the syscall. Used by
|
||||
// upstream net/file_wasip1.go to hand a socket FD off from an os.File
|
||||
// to a net.Listener / net.Conn.
|
||||
func (fd *FD) Copy() FD {
|
||||
return FD{
|
||||
Sysfd: fd.Sysfd,
|
||||
SysFile: fd.SysFile.ref(),
|
||||
IsStream: fd.IsStream,
|
||||
ZeroReadIsEOF: fd.ZeroReadIsEOF,
|
||||
}
|
||||
}
|
||||
|
||||
// Close marks the FD closed. The underlying syscall FD is only released
|
||||
// when the refcount drops to zero — earlier Close calls (e.g. on the
|
||||
// os.File side after a successful net.FileConn handoff) just decrement.
|
||||
func (fd *FD) Close() error {
|
||||
if fd.closed {
|
||||
return ErrFileClosing
|
||||
}
|
||||
fd.closed = true
|
||||
if !fd.SysFile.destroy() {
|
||||
return nil
|
||||
}
|
||||
return syscall.Close(fd.Sysfd)
|
||||
}
|
||||
|
||||
// Exist reports whether fd points to an actual FD wrapper (non-nil).
|
||||
// Callers in package os hold a *FD via a per-target alias and need to
|
||||
// nil-check without depending on the concrete type being a pointer.
|
||||
func (fd *FD) Exist() bool { return fd != nil }
|
||||
|
||||
// CloseFunc is the hook upstream net's poll package exposes so tests
|
||||
// can intercept Close. We just point at syscall.Close.
|
||||
var CloseFunc func(int) error = syscall.Close
|
||||
|
||||
// String is an internal string definition for methods/functions that
|
||||
// shouldn't be used outside the stdlib. Upstream net references it
|
||||
// from rawconn.go to mark methods as not-for-external-use.
|
||||
type String string
|
||||
|
||||
// RawControl / RawRead / RawWrite back syscall.RawConn's three callback
|
||||
// methods. They invoke f with the underlying FD; the bool return of
|
||||
// RawRead / RawWrite controls retry-on-EAGAIN, which we implement by
|
||||
// parking the goroutine on the netpoll registry and retrying until f
|
||||
// returns true (the same loop upstream uses).
|
||||
func (fd *FD) RawControl(f func(uintptr)) error {
|
||||
if fd.closed {
|
||||
return ErrFileClosing
|
||||
}
|
||||
f(uintptr(fd.Sysfd))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fd *FD) RawRead(f func(uintptr) bool) error {
|
||||
if fd.closed {
|
||||
return ErrFileClosing
|
||||
}
|
||||
for {
|
||||
if f(uintptr(fd.Sysfd)) {
|
||||
return nil
|
||||
}
|
||||
wait(fd.Sysfd, pollModeRead)
|
||||
}
|
||||
}
|
||||
|
||||
func (fd *FD) RawWrite(f func(uintptr) bool) error {
|
||||
if fd.closed {
|
||||
return ErrFileClosing
|
||||
}
|
||||
for {
|
||||
if f(uintptr(fd.Sysfd)) {
|
||||
return nil
|
||||
}
|
||||
wait(fd.Sysfd, pollModeWrite)
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown calls wasi sock_shutdown. how is one of syscall.SHUT_RD,
|
||||
// SHUT_WR, SHUT_RDWR.
|
||||
func (fd *FD) Shutdown(how int) error {
|
||||
return syscall.Shutdown(fd.Sysfd, how)
|
||||
}
|
||||
|
||||
// Accept loops over wasi sock_accept, parking the goroutine on EAGAIN
|
||||
// (waiting for a new connection) through the netpoll registry. Returns
|
||||
// (newfd, sockaddr=nil, errcall, error) — sockaddr is always nil because
|
||||
// wasi sock_accept doesn't return one.
|
||||
func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) {
|
||||
deadline := fd.rDeadline
|
||||
for {
|
||||
if !deadline.IsZero() && !time.Now().Before(deadline) {
|
||||
return -1, nil, "accept", ErrDeadlineExceeded
|
||||
}
|
||||
nfd, _, err := syscall.Accept(fd.Sysfd)
|
||||
if err == nil {
|
||||
return nfd, nil, "", nil
|
||||
}
|
||||
if err == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
if err != syscall.EAGAIN {
|
||||
return -1, nil, "accept", err
|
||||
}
|
||||
if deadline.IsZero() {
|
||||
wait(fd.Sysfd, pollModeRead)
|
||||
} else {
|
||||
if perr := fd.parkUntil(pollModeRead, deadline); perr != nil {
|
||||
return -1, nil, "accept", perr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isSocket reports whether the cached Filetype is a stream or datagram
|
||||
// socket. False if Init was never called (Filetype stays 0 = UNKNOWN).
|
||||
func (fd *FD) isSocket() bool {
|
||||
ft := syscall.Filetype(fd.SysFile.Filetype)
|
||||
return ft == syscall.FILETYPE_SOCKET_STREAM || ft == syscall.FILETYPE_SOCKET_DGRAM
|
||||
}
|
||||
|
||||
// Read reads from the FD into p. Sockets dispatch to sock_recv (which
|
||||
// honours fd.rDeadline directly); regular files/pipes go through
|
||||
// syscall.Read for the no-deadline fast path or readWithDeadline when
|
||||
// a deadline is set.
|
||||
func (fd *FD) Read(p []byte) (int, error) {
|
||||
if fd.closed {
|
||||
return 0, ErrFileClosing
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if fd.isSocket() {
|
||||
return fd.sockRecv(p)
|
||||
}
|
||||
if fd.rDeadline.IsZero() {
|
||||
return syscall.Read(fd.Sysfd, p)
|
||||
}
|
||||
return fd.readWithDeadline(p)
|
||||
}
|
||||
|
||||
// Write writes p to the FD. Sockets dispatch to sock_send. Regular
|
||||
// files / pipes go through syscall.Write or writeWithDeadline.
|
||||
func (fd *FD) Write(p []byte) (int, error) {
|
||||
if fd.closed {
|
||||
return 0, ErrFileClosing
|
||||
}
|
||||
if fd.isSocket() {
|
||||
return fd.sockSend(p)
|
||||
}
|
||||
if fd.wDeadline.IsZero() {
|
||||
return syscall.Write(fd.Sysfd, p)
|
||||
}
|
||||
return fd.writeWithDeadline(p)
|
||||
}
|
||||
|
||||
// Pread reads from the FD at the given offset. Always file semantics —
|
||||
// sockets aren't seekable so this never goes through sock_recv.
|
||||
func (fd *FD) Pread(p []byte, off int64) (int, error) {
|
||||
if fd.closed {
|
||||
return 0, ErrFileClosing
|
||||
}
|
||||
return syscall.Pread(fd.Sysfd, p, off)
|
||||
}
|
||||
|
||||
// Pwrite writes to the FD at the given offset.
|
||||
func (fd *FD) Pwrite(p []byte, off int64) (int, error) {
|
||||
if fd.closed {
|
||||
return 0, ErrFileClosing
|
||||
}
|
||||
return syscall.Pwrite(fd.Sysfd, p, off)
|
||||
}
|
||||
|
||||
// SetDeadline sets both the read and write deadlines.
|
||||
func (fd *FD) SetDeadline(t time.Time) error {
|
||||
fd.rDeadline = t
|
||||
fd.wDeadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls. A zero t
|
||||
// clears the deadline.
|
||||
func (fd *FD) SetReadDeadline(t time.Time) error {
|
||||
fd.rDeadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the deadline for future Write calls.
|
||||
func (fd *FD) SetWriteDeadline(t time.Time) error {
|
||||
fd.wDeadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
// readWithDeadline implements the EAGAIN→park retry loop with deadline
|
||||
// cancellation for non-socket FDs. The deadline is captured at function
|
||||
// entry; later SetReadDeadline calls don't affect this in-flight Read.
|
||||
func (fd *FD) readWithDeadline(p []byte) (int, error) {
|
||||
deadline := fd.rDeadline
|
||||
iov := wasiIovec{buf: &p[0], bufLen: uint32(len(p))}
|
||||
for {
|
||||
if !time.Now().Before(deadline) {
|
||||
return 0, ErrDeadlineExceeded
|
||||
}
|
||||
var n uint32
|
||||
errno := wasi_fd_read(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, unsafe.Pointer(&n))
|
||||
switch errno {
|
||||
case 0:
|
||||
return int(n), nil
|
||||
case wasiErrnoIntr:
|
||||
continue
|
||||
case wasiErrnoAgain:
|
||||
if err := fd.parkUntil(pollModeRead, deadline); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
return 0, syscall.Errno(errno)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fd *FD) writeWithDeadline(p []byte) (int, error) {
|
||||
deadline := fd.wDeadline
|
||||
var nn int
|
||||
for {
|
||||
if !time.Now().Before(deadline) {
|
||||
return nn, ErrDeadlineExceeded
|
||||
}
|
||||
if nn == len(p) {
|
||||
return nn, nil
|
||||
}
|
||||
buf := p[nn:]
|
||||
iov := wasiIovec{buf: &buf[0], bufLen: uint32(len(buf))}
|
||||
var n uint32
|
||||
errno := wasi_fd_write(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, unsafe.Pointer(&n))
|
||||
switch errno {
|
||||
case 0:
|
||||
nn += int(n)
|
||||
case wasiErrnoIntr:
|
||||
// retry
|
||||
case wasiErrnoAgain:
|
||||
if err := fd.parkUntil(pollModeWrite, deadline); err != nil {
|
||||
return nn, err
|
||||
}
|
||||
default:
|
||||
return nn, syscall.Errno(errno)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sockRecv is the socket sibling of readWithDeadline / syscall.Read.
|
||||
// Always issues sock_recv, regardless of deadline state, so callers
|
||||
// that don't want fd_read on a socket get a guaranteed sock_recv path.
|
||||
func (fd *FD) sockRecv(p []byte) (int, error) {
|
||||
deadline := fd.rDeadline
|
||||
iov := wasiIovec{buf: &p[0], bufLen: uint32(len(p))}
|
||||
for {
|
||||
if !deadline.IsZero() && !time.Now().Before(deadline) {
|
||||
return 0, ErrDeadlineExceeded
|
||||
}
|
||||
var n uint32
|
||||
var roFlags uint32
|
||||
errno := wasi_sock_recv(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, 0, unsafe.Pointer(&n), unsafe.Pointer(&roFlags))
|
||||
switch errno {
|
||||
case 0:
|
||||
return int(n), nil
|
||||
case wasiErrnoIntr:
|
||||
continue
|
||||
case wasiErrnoAgain:
|
||||
if deadline.IsZero() {
|
||||
wait(fd.Sysfd, pollModeRead)
|
||||
} else if err := fd.parkUntil(pollModeRead, deadline); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
return 0, syscall.Errno(errno)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fd *FD) sockSend(p []byte) (int, error) {
|
||||
deadline := fd.wDeadline
|
||||
var nn int
|
||||
for {
|
||||
if !deadline.IsZero() && !time.Now().Before(deadline) {
|
||||
return nn, ErrDeadlineExceeded
|
||||
}
|
||||
if nn == len(p) {
|
||||
return nn, nil
|
||||
}
|
||||
buf := p[nn:]
|
||||
iov := wasiIovec{buf: &buf[0], bufLen: uint32(len(buf))}
|
||||
var n uint32
|
||||
errno := wasi_sock_send(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, 0, unsafe.Pointer(&n))
|
||||
switch errno {
|
||||
case 0:
|
||||
nn += int(n)
|
||||
case wasiErrnoIntr:
|
||||
// retry
|
||||
case wasiErrnoAgain:
|
||||
if deadline.IsZero() {
|
||||
wait(fd.Sysfd, pollModeWrite)
|
||||
} else if err := fd.parkUntil(pollModeWrite, deadline); err != nil {
|
||||
return nn, err
|
||||
}
|
||||
default:
|
||||
return nn, syscall.Errno(errno)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wasi snapshot preview1 errno values, kept here so the read/write loops
|
||||
// can compare without dragging in the full syscall errno table for a
|
||||
// switch on three constants. These match the wasi spec exactly; see also
|
||||
// syscall_libc_wasi.go in package syscall.
|
||||
const (
|
||||
wasiErrnoAgain uint32 = 6
|
||||
wasiErrnoIntr uint32 = 27
|
||||
)
|
||||
|
||||
// wait parks the current goroutine until the FD becomes ready in the
|
||||
// given direction. No deadline; mirrors the helper of the same name in
|
||||
// package syscall (intentionally duplicated rather than linknamed
|
||||
// across the package boundary — see project memory on shim avoidance).
|
||||
func wait(fd int, mode uint8) {
|
||||
pd := runtime_netpoll_addwait(uint32(fd), mode)
|
||||
task.Pause()
|
||||
runtime_netpoll_done(pd)
|
||||
}
|
||||
|
||||
// parkUntil parks the current goroutine on (fd, mode) with a deadline.
|
||||
// Returns nil if the FD became ready or the timer fired (caller's loop
|
||||
// re-checks the deadline at top); ErrDeadlineExceeded if the deadline
|
||||
// was already in the past.
|
||||
//
|
||||
// Race handling: the deadline timer's callback and pollIO's event walk
|
||||
// can both target the same pollDesc. The pd.fired flag guards against
|
||||
// double-pushing the task to the run queue; whichever arrives second
|
||||
// is a no-op.
|
||||
func (fd *FD) parkUntil(mode uint8, deadline time.Time) error {
|
||||
d := time.Until(deadline)
|
||||
if d <= 0 {
|
||||
return ErrDeadlineExceeded
|
||||
}
|
||||
pd := runtime_netpoll_addwait(uint32(fd.Sysfd), mode)
|
||||
timer := time.AfterFunc(d, func() {
|
||||
runtime_netpoll_wake(pd)
|
||||
})
|
||||
task.Pause()
|
||||
timer.Stop()
|
||||
runtime_netpoll_done(pd)
|
||||
return nil
|
||||
}
|
||||
@@ -453,10 +453,7 @@ func rawStructFieldFromPointer(descriptor *structType, fieldType *RawType, data
|
||||
data = unsafe.Add(data, 1) // C: data+1
|
||||
tagLen := uintptr(*(*byte)(data))
|
||||
data = unsafe.Add(data, 1) // C: data+1
|
||||
tag = *(*string)(unsafe.Pointer(&stringHeader{
|
||||
data: data,
|
||||
len: tagLen,
|
||||
}))
|
||||
tag = unsafe.String((*byte)(data), tagLen)
|
||||
}
|
||||
|
||||
// Set the PkgPath to some (arbitrary) value if the package path is not
|
||||
@@ -757,6 +754,98 @@ func (t *RawType) FieldAlign() int {
|
||||
return t.Align()
|
||||
}
|
||||
|
||||
// ConvertibleTo returns whether a value of type t can be converted to a variable of type u
|
||||
func (r *RawType) ConvertibleTo(u *RawType) bool {
|
||||
|
||||
// This logic is mostly copied from Value.CanConvert
|
||||
|
||||
switch r.Kind() {
|
||||
case Int, Int8, Int16, Int32, Int64:
|
||||
switch u.Kind() {
|
||||
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
||||
return true
|
||||
case Float32, Float64:
|
||||
return true
|
||||
case String:
|
||||
return true
|
||||
}
|
||||
|
||||
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
||||
switch u.Kind() {
|
||||
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
||||
return true
|
||||
case Float32, Float64:
|
||||
return true
|
||||
case String:
|
||||
return true
|
||||
}
|
||||
|
||||
case Float32, Float64:
|
||||
switch u.Kind() {
|
||||
case Int, Int8, Int16, Int32, Int64:
|
||||
return true
|
||||
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
||||
return true
|
||||
case Float32, Float64:
|
||||
return true
|
||||
}
|
||||
|
||||
case Complex64, Complex128:
|
||||
switch u.Kind() {
|
||||
case Complex64, Complex128:
|
||||
return true
|
||||
}
|
||||
|
||||
case Slice:
|
||||
switch u.Kind() {
|
||||
case Array:
|
||||
// This may fail at runtime if there isn't room
|
||||
if r.elem() == u.elem() {
|
||||
return true
|
||||
}
|
||||
|
||||
case Pointer:
|
||||
// This may fail at runtime if there isn't room
|
||||
if u.elem().Kind() == Array && r.elem() == u.elem().elem() {
|
||||
return true
|
||||
}
|
||||
|
||||
case String:
|
||||
// bytes or runes
|
||||
if !r.elem().isNamed() && (r.elem().Kind() == Uint8 || r.elem().Kind() == Int32) {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case String:
|
||||
// bytes or runes
|
||||
if u.Kind() == Slice && !u.elem().isNamed() && (u.elem().Kind() == Uint8 || u.elem().Kind() == Int32) {
|
||||
return true
|
||||
}
|
||||
|
||||
case Pointer:
|
||||
if !r.isNamed() && u.Kind() == Pointer && !u.isNamed() && r.elem().underlying() == u.elem().underlying() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if r.underlying() == u.underlying() {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Kind() == Interface && u.NumMethod() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO(dgryski): Unimplemented
|
||||
// struct types
|
||||
// channels
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// AssignableTo returns whether a value of type t can be assigned to a variable
|
||||
// of type u.
|
||||
func (t *RawType) AssignableTo(u Type) bool {
|
||||
@@ -930,10 +1019,7 @@ func readStringZ(data unsafe.Pointer) string {
|
||||
data = unsafe.Add(data, 1) // C: data++
|
||||
}
|
||||
|
||||
return *(*string)(unsafe.Pointer(&stringHeader{
|
||||
data: start,
|
||||
len: len,
|
||||
}))
|
||||
return unsafe.String((*byte)(start), len)
|
||||
}
|
||||
|
||||
func (t *RawType) name() string {
|
||||
|
||||
+201
-103
@@ -51,6 +51,22 @@ func (v Value) isRO() bool {
|
||||
return v.flags&(valueFlagRO) != 0
|
||||
}
|
||||
|
||||
// These are package methods, not methods on Value, since the reflect.Value embeds a reflectlite.Value, so any
|
||||
// methods added to reflectlite.Value are visible to the user.
|
||||
|
||||
func IsRO(v Value) bool {
|
||||
return v.isRO()
|
||||
}
|
||||
|
||||
func MakeRO(v Value, ro bool) Value {
|
||||
if ro {
|
||||
v.flags |= valueFlagRO
|
||||
} else {
|
||||
v.flags &^= valueFlagRO
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (v Value) checkRO() {
|
||||
if v.isRO() {
|
||||
panic("reflect: value is not settable")
|
||||
@@ -146,6 +162,19 @@ func TypeAssert[T any](v Value) (T, bool) {
|
||||
|
||||
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
|
||||
// be subject to the isExported check.
|
||||
// loadSmallValue loads a value of size <= sizeof(uintptr) from ptr into
|
||||
// a pointer-sized value suitable for storing in an interface's data field.
|
||||
func loadSmallValue(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
if size == unsafe.Sizeof(uintptr(0)) {
|
||||
return *(*unsafe.Pointer)(ptr)
|
||||
}
|
||||
var value uintptr
|
||||
for j := size; j != 0; j-- {
|
||||
value = (value << 8) | uintptr(*(*uint8)(unsafe.Add(ptr, j-1)))
|
||||
}
|
||||
return unsafe.Pointer(value)
|
||||
}
|
||||
|
||||
func valueInterfaceUnsafe(v Value) interface{} {
|
||||
if v.typecode.Kind() == Interface {
|
||||
// The value itself is an interface. This can happen when getting the
|
||||
@@ -158,11 +187,7 @@ func valueInterfaceUnsafe(v Value) interface{} {
|
||||
if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||
// Value was indirect but must be put back directly in the interface
|
||||
// value.
|
||||
var value uintptr
|
||||
for j := v.typecode.Size(); j != 0; j-- {
|
||||
value = (value << 8) | uintptr(*(*uint8)(unsafe.Add(v.value, j-1)))
|
||||
}
|
||||
v.value = unsafe.Pointer(value)
|
||||
v.value = loadSmallValue(v.value, v.typecode.Size())
|
||||
}
|
||||
return composeInterface(unsafe.Pointer(v.typecode), v.value)
|
||||
}
|
||||
@@ -713,20 +738,12 @@ func (v Value) Slice(i, j int) Value {
|
||||
|
||||
case String:
|
||||
i, j := uintptr(i), uintptr(j)
|
||||
str := *(*stringHeader)(v.value)
|
||||
|
||||
if j < i || str.len < j {
|
||||
slicePanic()
|
||||
}
|
||||
|
||||
hdr := stringHeader{
|
||||
data: unsafe.Add(str.data, i),
|
||||
len: j - i,
|
||||
}
|
||||
str := *(*string)(v.value)
|
||||
sliced := str[i:j]
|
||||
|
||||
return Value{
|
||||
typecode: v.typecode,
|
||||
value: unsafe.Pointer(&hdr),
|
||||
value: unsafe.Pointer(&sliced),
|
||||
flags: v.flags,
|
||||
}
|
||||
}
|
||||
@@ -801,7 +818,7 @@ func (v Value) Len() int {
|
||||
case Slice:
|
||||
return int((*sliceHeader)(v.value).len)
|
||||
case String:
|
||||
return int((*stringHeader)(v.value).len)
|
||||
return len(*(*string)(v.value))
|
||||
default:
|
||||
panic(&ValueError{Method: "Len", Kind: v.Kind()})
|
||||
}
|
||||
@@ -977,13 +994,10 @@ func (v Value) Index(i int) Value {
|
||||
// Keeping valueFlagExported if set, but don't set valueFlagIndirect
|
||||
// otherwise CanSet will return true for string elements (which is bad,
|
||||
// strings are read-only).
|
||||
s := *(*stringHeader)(v.value)
|
||||
if uint(i) >= uint(s.len) {
|
||||
panic("reflect: string index out of range")
|
||||
}
|
||||
s := *(*string)(v.value)
|
||||
return Value{
|
||||
typecode: uint8Type,
|
||||
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Add(s.data, i)))),
|
||||
value: unsafe.Pointer(uintptr(s[i])),
|
||||
flags: v.flags & valueFlagExported,
|
||||
}
|
||||
case Array:
|
||||
@@ -1085,18 +1099,8 @@ func (v Value) MapKeys() []Value {
|
||||
k := New(v.typecode.Key())
|
||||
e := New(v.typecode.Elem())
|
||||
|
||||
keyType := v.typecode.key()
|
||||
keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
|
||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
|
||||
|
||||
for hashmapNext(v.pointer(), it, k.value, e.value) {
|
||||
if shouldUnpackInterface {
|
||||
intf := *(*interface{})(k.value)
|
||||
v := ValueOf(intf)
|
||||
keys = append(keys, v)
|
||||
} else {
|
||||
keys = append(keys, k.Elem())
|
||||
}
|
||||
keys = append(keys, k.Elem())
|
||||
k = New(v.typecode.Key())
|
||||
}
|
||||
|
||||
@@ -1109,9 +1113,40 @@ func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueS
|
||||
//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet
|
||||
func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
|
||||
|
||||
//go:linkname hashmapInterfaceGet runtime.hashmapInterfaceGet
|
||||
func hashmapInterfaceGet(m unsafe.Pointer, key interface{}, value unsafe.Pointer, valueSize uintptr) bool
|
||||
//go:linkname hashmapGenericGet runtime.hashmapGenericGet
|
||||
func hashmapGenericGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
|
||||
|
||||
// genericKeyPtr returns a pointer to key data suitable for passing to the
|
||||
// hashmapGeneric* functions. When the map's key type is an interface,
|
||||
// special handling is needed: if the key Value already holds an interface
|
||||
// (e.g. from MapKeys iteration), its memory already contains the
|
||||
// {typecode, data} pair the hashmap expects, so we use it directly.
|
||||
// If the key is a concrete type being assigned to an interface-keyed map,
|
||||
// we compose the interface first.
|
||||
func genericKeyPtr(vkey *RawType, key Value) unsafe.Pointer {
|
||||
if vkey.Kind() == Interface {
|
||||
if key.Kind() == Interface {
|
||||
// Key is already an interface value stored indirectly;
|
||||
// key.value points to {typecode, data}.
|
||||
return key.value
|
||||
}
|
||||
// Concrete value being used as an interface key.
|
||||
// For small addressable values, key.value is a pointer to
|
||||
// the data, but the interface value field stores the data
|
||||
// directly; load it using the same endian-safe approach as
|
||||
// valueInterfaceUnsafe.
|
||||
val := key.value
|
||||
if key.isIndirect() && key.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||
val = loadSmallValue(key.value, key.typecode.Size())
|
||||
}
|
||||
intf := composeInterface(unsafe.Pointer(key.typecode), val)
|
||||
return unsafe.Pointer(&intf)
|
||||
}
|
||||
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||
return key.value
|
||||
}
|
||||
return unsafe.Pointer(&key.value)
|
||||
}
|
||||
func (v Value) MapIndex(key Value) Value {
|
||||
if v.Kind() != Map {
|
||||
panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
|
||||
@@ -1140,13 +1175,16 @@ func (v Value) MapIndex(key Value) Value {
|
||||
} else {
|
||||
keyptr = unsafe.Pointer(&key.value)
|
||||
}
|
||||
//TODO(dgryski): zero out padding bytes in key, if any
|
||||
if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
|
||||
return Value{}
|
||||
}
|
||||
return elem.Elem()
|
||||
} else {
|
||||
if ok := hashmapInterfaceGet(v.pointer(), key.Interface(), elem.value, elemType.Size()); !ok {
|
||||
// Compiler-generated hash/equal path: keys are stored at their
|
||||
// actual type. Use hashmapGenericGet which dispatches through the
|
||||
// map's own keyHash/keyEqual function pointers.
|
||||
keyptr := genericKeyPtr(vkey, key)
|
||||
if ok := hashmapGenericGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
|
||||
return Value{}
|
||||
}
|
||||
return elem.Elem()
|
||||
@@ -1171,8 +1209,7 @@ type MapIter struct {
|
||||
key Value
|
||||
val Value
|
||||
|
||||
valid bool
|
||||
unpackKeyInterface bool
|
||||
valid bool
|
||||
}
|
||||
|
||||
func (it *MapIter) Key() Value {
|
||||
@@ -1180,12 +1217,6 @@ func (it *MapIter) Key() Value {
|
||||
panic("reflect.MapIter.Key called on invalid iterator")
|
||||
}
|
||||
|
||||
if it.unpackKeyInterface {
|
||||
intf := *(*interface{})(it.key.value)
|
||||
v := ValueOf(intf)
|
||||
return v
|
||||
}
|
||||
|
||||
return it.key.Elem()
|
||||
}
|
||||
|
||||
@@ -1218,15 +1249,9 @@ func (iter *MapIter) Reset(v Value) {
|
||||
panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
|
||||
}
|
||||
|
||||
keyType := v.typecode.key()
|
||||
|
||||
keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
|
||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
|
||||
|
||||
*iter = MapIter{
|
||||
m: v,
|
||||
it: hashmapNewIterator(),
|
||||
unpackKeyInterface: shouldUnpackInterface,
|
||||
m: v,
|
||||
it: hashmapNewIterator(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1476,13 +1501,11 @@ func convertOp(src Value, typ Type) (Value, bool) {
|
||||
return cvtFloat(src, rtype), true
|
||||
}
|
||||
|
||||
/*
|
||||
case Complex64, Complex128:
|
||||
switch src.Kind() {
|
||||
case Complex64, Complex128:
|
||||
return cvtComplex
|
||||
}
|
||||
*/
|
||||
case Complex64, Complex128:
|
||||
switch rtype := typ.(*RawType); rtype.Kind() {
|
||||
case Complex64, Complex128:
|
||||
return cvtComplex(src, rtype), true
|
||||
}
|
||||
|
||||
case Slice:
|
||||
switch rtype := typ.(*RawType); rtype.Kind() {
|
||||
@@ -1525,6 +1548,12 @@ func convertOp(src Value, typ Type) (Value, bool) {
|
||||
return cvtStringRunes(src, rtype), true
|
||||
}
|
||||
}
|
||||
|
||||
case Pointer:
|
||||
rtype := typ.(*RawType)
|
||||
if rtype.Kind() == Pointer && !src.typecode.isNamed() && !rtype.isNamed() && src.typecode.elem().underlying() == rtype.elem().underlying() {
|
||||
return cvtDirect(src, rtype), true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(dgryski): Unimplemented:
|
||||
@@ -1569,6 +1598,18 @@ func cvtFloat(v Value, t *RawType) Value {
|
||||
return makeFloat(v.flags, v.Float(), t)
|
||||
}
|
||||
|
||||
func cvtDirect(v Value, t *RawType) Value {
|
||||
return Value{
|
||||
typecode: t,
|
||||
value: v.value,
|
||||
flags: v.flags,
|
||||
}
|
||||
}
|
||||
|
||||
func cvtComplex(v Value, t *RawType) Value {
|
||||
return makeComplex(v.flags, v.Complex(), t)
|
||||
}
|
||||
|
||||
//go:linkname stringToBytes runtime.stringToBytes
|
||||
func stringToBytes(x string) []byte
|
||||
|
||||
@@ -1652,20 +1693,76 @@ func makeFloat32(flags valueFlags, f float32, t *RawType) Value {
|
||||
return v
|
||||
}
|
||||
|
||||
func cvtIntString(src Value, t *RawType) Value {
|
||||
panic("cvtUintString: unimplemented")
|
||||
func makeComplex(flags valueFlags, f complex128, t *RawType) Value {
|
||||
size := t.Size()
|
||||
|
||||
v := Value{
|
||||
typecode: t,
|
||||
flags: flags,
|
||||
}
|
||||
|
||||
ptr := unsafe.Pointer(&v.value)
|
||||
if size > unsafe.Sizeof(uintptr(0)) {
|
||||
ptr = alloc(size, nil)
|
||||
v.value = ptr
|
||||
}
|
||||
|
||||
switch size {
|
||||
case 8:
|
||||
*(*complex64)(ptr) = complex64(f)
|
||||
case 16:
|
||||
*(*complex128)(ptr) = f
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func cvtUintString(src Value, t *RawType) Value {
|
||||
panic("cvtUintString: unimplemented")
|
||||
func cvtIntString(v Value, t *RawType) Value {
|
||||
s := "\uFFFD"
|
||||
if x := v.Int(); int64(rune(x)) == x {
|
||||
s = string(rune(x))
|
||||
}
|
||||
return Value{
|
||||
typecode: t,
|
||||
value: unsafe.Pointer(&s),
|
||||
flags: v.flags,
|
||||
}
|
||||
}
|
||||
|
||||
func cvtStringRunes(src Value, t *RawType) Value {
|
||||
panic("cvsStringRunes: unimplemented")
|
||||
func cvtUintString(v Value, t *RawType) Value {
|
||||
s := "\uFFFD"
|
||||
if x := v.Uint(); uint64(rune(x)) == x {
|
||||
s = string(rune(x))
|
||||
}
|
||||
|
||||
return Value{
|
||||
typecode: t,
|
||||
value: unsafe.Pointer(&s),
|
||||
flags: v.flags,
|
||||
}
|
||||
}
|
||||
|
||||
func cvtRunesString(src Value, t *RawType) Value {
|
||||
panic("cvsRunesString: unimplemented")
|
||||
//go:linkname stringToRunes runtime.stringToRunes
|
||||
func stringToRunes(s string) []rune
|
||||
|
||||
func cvtStringRunes(v Value, t *RawType) Value {
|
||||
b := stringToRunes(*(*string)(v.value))
|
||||
return Value{
|
||||
typecode: t,
|
||||
value: unsafe.Pointer(&b),
|
||||
flags: v.flags,
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname stringFromRunes runtime.stringFromRunes
|
||||
func stringFromRunes(r []rune) string
|
||||
|
||||
func cvtRunesString(v Value, t *RawType) Value {
|
||||
s := stringFromRunes(*(*[]rune)(v.value))
|
||||
return Value{
|
||||
typecode: t,
|
||||
value: unsafe.Pointer(&s),
|
||||
flags: v.flags,
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname slicePanic runtime.slicePanic
|
||||
@@ -1714,8 +1811,7 @@ const zerobufferLen = 32
|
||||
func init() {
|
||||
// 32 characters of zero bytes
|
||||
zerobufferStr := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
s := (*stringHeader)(unsafe.Pointer(&zerobufferStr))
|
||||
zerobuffer = s.data
|
||||
zerobuffer = unsafe.Pointer(unsafe.StringData(zerobufferStr))
|
||||
}
|
||||
|
||||
func Zero(typ Type) Value {
|
||||
@@ -1766,19 +1862,11 @@ type sliceHeader struct {
|
||||
cap uintptr
|
||||
}
|
||||
|
||||
// Like sliceHeader, this type is used internally to make sure pointer and
|
||||
// non-pointer fields match those of actual strings.
|
||||
type stringHeader struct {
|
||||
data unsafe.Pointer
|
||||
len uintptr
|
||||
}
|
||||
|
||||
// Verify SliceHeader and StringHeader sizes.
|
||||
// Verify SliceHeader size.
|
||||
// See https://github.com/tinygo-org/tinygo/pull/4156
|
||||
// and https://github.com/tinygo-org/tinygo/issues/1284.
|
||||
var (
|
||||
_ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(sliceHeader{})]byte{}
|
||||
_ [unsafe.Sizeof("")]byte = [unsafe.Sizeof(stringHeader{})]byte{}
|
||||
)
|
||||
|
||||
type ValueError struct {
|
||||
@@ -1845,29 +1933,29 @@ func Copy(dst, src Value) int {
|
||||
|
||||
func buflen(v Value) (unsafe.Pointer, uintptr) {
|
||||
var buf unsafe.Pointer
|
||||
var len uintptr
|
||||
var length uintptr
|
||||
switch v.typecode.Kind() {
|
||||
case Slice:
|
||||
hdr := (*sliceHeader)(v.value)
|
||||
buf = hdr.data
|
||||
len = hdr.len
|
||||
length = hdr.len
|
||||
case Array:
|
||||
if v.isIndirect() || v.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||
buf = v.value
|
||||
} else {
|
||||
buf = unsafe.Pointer(&v.value)
|
||||
}
|
||||
len = uintptr(v.Len())
|
||||
length = uintptr(v.Len())
|
||||
case String:
|
||||
hdr := (*stringHeader)(v.value)
|
||||
buf = hdr.data
|
||||
len = hdr.len
|
||||
s := *(*string)(v.value)
|
||||
buf = unsafe.Pointer(unsafe.StringData(s))
|
||||
length = uintptr(len(s))
|
||||
default:
|
||||
// This shouldn't happen
|
||||
panic("reflect.Copy: not slice or array or string")
|
||||
}
|
||||
|
||||
return buf, len
|
||||
return buf, length
|
||||
}
|
||||
|
||||
//go:linkname sliceGrow runtime.sliceGrow
|
||||
@@ -1969,8 +2057,8 @@ func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer)
|
||||
//go:linkname hashmapBinarySet runtime.hashmapBinarySet
|
||||
func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer)
|
||||
|
||||
//go:linkname hashmapInterfaceSet runtime.hashmapInterfaceSet
|
||||
func hashmapInterfaceSet(m unsafe.Pointer, key interface{}, value unsafe.Pointer)
|
||||
//go:linkname hashmapGenericSet runtime.hashmapGenericSet
|
||||
func hashmapGenericSet(m unsafe.Pointer, key, value unsafe.Pointer)
|
||||
|
||||
//go:linkname hashmapStringDelete runtime.hashmapStringDelete
|
||||
func hashmapStringDelete(m unsafe.Pointer, key string)
|
||||
@@ -1978,8 +2066,8 @@ func hashmapStringDelete(m unsafe.Pointer, key string)
|
||||
//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDelete
|
||||
func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer)
|
||||
|
||||
//go:linkname hashmapInterfaceDelete runtime.hashmapInterfaceDelete
|
||||
func hashmapInterfaceDelete(m unsafe.Pointer, key interface{})
|
||||
//go:linkname hashmapGenericDelete runtime.hashmapGenericDelete
|
||||
func hashmapGenericDelete(m unsafe.Pointer, key unsafe.Pointer)
|
||||
|
||||
func (v Value) SetMapIndex(key, elem Value) {
|
||||
v.checkRO()
|
||||
@@ -2002,15 +2090,19 @@ func (v Value) SetMapIndex(key, elem Value) {
|
||||
}
|
||||
|
||||
// make elem an interface if it needs to be converted
|
||||
if v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
|
||||
intf := composeInterface(unsafe.Pointer(elem.typecode), elem.value)
|
||||
if !del && v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
|
||||
val := elem.value
|
||||
if elem.isIndirect() && elem.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||
val = loadSmallValue(elem.value, elem.typecode.Size())
|
||||
}
|
||||
intf := composeInterface(unsafe.Pointer(elem.typecode), val)
|
||||
elem = Value{
|
||||
typecode: v.typecode.elem(),
|
||||
value: unsafe.Pointer(&intf),
|
||||
}
|
||||
}
|
||||
|
||||
if key.Kind() == String {
|
||||
if vkey.Kind() == String {
|
||||
if del {
|
||||
hashmapStringDelete(v.pointer(), *(*string)(key.value))
|
||||
} else {
|
||||
@@ -2023,7 +2115,7 @@ func (v Value) SetMapIndex(key, elem Value) {
|
||||
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
|
||||
}
|
||||
|
||||
} else if key.typecode.isBinary() {
|
||||
} else if vkey.isBinary() {
|
||||
var keyptr unsafe.Pointer
|
||||
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||
keyptr = key.value
|
||||
@@ -2043,8 +2135,11 @@ func (v Value) SetMapIndex(key, elem Value) {
|
||||
hashmapBinarySet(v.pointer(), keyptr, elemptr)
|
||||
}
|
||||
} else {
|
||||
// Compiler-generated hash/equal path.
|
||||
keyptr := genericKeyPtr(vkey, key)
|
||||
|
||||
if del {
|
||||
hashmapInterfaceDelete(v.pointer(), key.Interface())
|
||||
hashmapGenericDelete(v.pointer(), keyptr)
|
||||
} else {
|
||||
var elemptr unsafe.Pointer
|
||||
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||
@@ -2053,7 +2148,7 @@ func (v Value) SetMapIndex(key, elem Value) {
|
||||
elemptr = unsafe.Pointer(&elem.value)
|
||||
}
|
||||
|
||||
hashmapInterfaceSet(v.pointer(), key.Interface(), elemptr)
|
||||
hashmapGenericSet(v.pointer(), keyptr, elemptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2110,6 +2205,9 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value {
|
||||
//go:linkname hashmapMake runtime.hashmapMake
|
||||
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer
|
||||
|
||||
//go:linkname hashmapMakeReflect runtime.hashmapMakeReflect
|
||||
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) unsafe.Pointer
|
||||
|
||||
// MakeMapWithSize creates a new map with the specified type and initial space
|
||||
// for approximately n elements.
|
||||
func MakeMapWithSize(typ Type, n int) Value {
|
||||
@@ -2118,7 +2216,6 @@ func MakeMapWithSize(typ Type, n int) Value {
|
||||
const (
|
||||
hashmapAlgorithmBinary uint8 = iota
|
||||
hashmapAlgorithmString
|
||||
hashmapAlgorithmInterface
|
||||
)
|
||||
|
||||
if typ.Kind() != Map {
|
||||
@@ -2132,18 +2229,19 @@ func MakeMapWithSize(typ Type, n int) Value {
|
||||
key := typ.Key().(*RawType)
|
||||
val := typ.Elem().(*RawType)
|
||||
|
||||
var alg uint8
|
||||
var m unsafe.Pointer
|
||||
|
||||
if key.Kind() == String {
|
||||
alg = hashmapAlgorithmString
|
||||
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmString)
|
||||
} else if key.isBinary() {
|
||||
alg = hashmapAlgorithmBinary
|
||||
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmBinary)
|
||||
} else {
|
||||
alg = hashmapAlgorithmInterface
|
||||
// Composite key type (struct with strings, floats, etc.).
|
||||
// Use runtime-generated hash/equal closures that walk the
|
||||
// type structure, matching the compiler-generated functions.
|
||||
m = hashmapMakeReflect(key.Size(), val.Size(), uintptr(n), unsafe.Pointer(key))
|
||||
}
|
||||
|
||||
m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg)
|
||||
|
||||
return Value{
|
||||
typecode: typ.(*RawType),
|
||||
value: m,
|
||||
|
||||
@@ -15,7 +15,7 @@ func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
libc_arc4random_buf(unsafe.Pointer(&p[0]), uint(len(p)))
|
||||
libc_arc4random_buf(unsafe.Pointer(unsafe.SliceData(p)), uint(len(p)))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -42,3 +42,8 @@ func SystemStack() uintptr {
|
||||
runtimePanic("scheduler is disabled")
|
||||
return 0 // unreachable
|
||||
}
|
||||
|
||||
func GoroutineStack() uintptr {
|
||||
// No separate goroutine stack without a scheduler.
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -11,3 +11,14 @@ uintptr_t SystemStack() {
|
||||
);
|
||||
return sp;
|
||||
}
|
||||
|
||||
uintptr_t GoroutineStack() {
|
||||
uintptr_t sp;
|
||||
asm volatile(
|
||||
"mrs %0, PSP"
|
||||
: "=r"(sp)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
return sp;
|
||||
}
|
||||
|
||||
@@ -70,3 +70,10 @@ func (s *state) pause() {
|
||||
//
|
||||
//export SystemStack
|
||||
func SystemStack() uintptr
|
||||
|
||||
// GoroutineStack returns the PSP (goroutine stack pointer). When a fault
|
||||
// occurs in goroutine context, the hardware saves the exception frame to PSP;
|
||||
// reading PSP+0x18 gives the actual faulting PC.
|
||||
//
|
||||
//export GoroutineStack
|
||||
func GoroutineStack() uintptr
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
//go:build badger2350
|
||||
|
||||
// Chip: RP2350A (QFN-60, 30 GPIO) -> target inherits from "rp2350".
|
||||
// Pin source: https://github.com/pimoroni/badger2350/blob/main/board/pins.csv
|
||||
//
|
||||
// NOTES (verify before first flash):
|
||||
// - POWER_EN (GPIO27) likely switches peripheral/display power.
|
||||
// Verify polarity against the schematic; pull HIGH early if needed,
|
||||
// otherwise the display will remain blank.
|
||||
// - CHARGE_STAT is connected to EXT_GPIO2 (I2C IO expander) according to pins.csv,
|
||||
// NOT the RP2350 -> intentionally not defined as a pin here.
|
||||
|
||||
package machine
|
||||
|
||||
// Crystal oscillator frequency.
|
||||
const xoscFreq = 12 // MHz
|
||||
|
||||
// User buttons.
|
||||
const (
|
||||
BUTTON_A Pin = GPIO7
|
||||
BUTTON_B Pin = GPIO9
|
||||
BUTTON_C Pin = GPIO10
|
||||
BUTTON_UP Pin = GPIO11
|
||||
BUTTON_DOWN Pin = GPIO6
|
||||
BUTTON_HOME Pin = GPIO22
|
||||
BUTTON_BOOT Pin = BUTTON_HOME
|
||||
|
||||
// System / RTC-related buttons.
|
||||
BUTTON_RESET Pin = GPIO14
|
||||
BUTTON_INT Pin = GPIO15
|
||||
)
|
||||
|
||||
// Case LEDs (4-zone mono illumination), named CL0..CL3 in the docs.
|
||||
const (
|
||||
CL0 Pin = GPIO0
|
||||
CL1 Pin = GPIO1
|
||||
CL2 Pin = GPIO2
|
||||
CL3 Pin = GPIO3
|
||||
|
||||
// Convention: first zone as default LED.
|
||||
LED = CL0
|
||||
)
|
||||
|
||||
// E-ink display (Inky), on SPI0.
|
||||
const (
|
||||
INKY_BUSY Pin = GPIO16
|
||||
INKY_CS Pin = GPIO17
|
||||
INKY_SCLK Pin = GPIO18
|
||||
INKY_MOSI Pin = GPIO19
|
||||
INKY_DC Pin = GPIO20
|
||||
INKY_RES Pin = GPIO21
|
||||
)
|
||||
|
||||
// SPI0 default pins == Inky bus. SDI is unused by the EPD,
|
||||
// but GPIO16 is a valid SPI0 RX line on the RP2350.
|
||||
const (
|
||||
SPI0_SCK_PIN = GPIO18 // INKY_SCLK
|
||||
SPI0_SDO_PIN = GPIO19 // INKY_MOSI
|
||||
SPI0_SDI_PIN = GPIO16 // (unused by the EPD)
|
||||
)
|
||||
|
||||
// SPI1 is not routed to any header; NoPin satisfies the machine package.
|
||||
const (
|
||||
SPI1_SCK_PIN Pin = NoPin
|
||||
SPI1_SDO_PIN Pin = NoPin
|
||||
SPI1_SDI_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// UART pins - default RP2350 UART0 mapping.
|
||||
// Note: GPIO0/GPIO1 are also CL0/CL1 (case LEDs)
|
||||
// Do not use UART0 and LEDs simultaneously.
|
||||
const (
|
||||
UART0_TX_PIN = GPIO0
|
||||
UART0_RX_PIN = GPIO1
|
||||
UART_TX_PIN = UART0_TX_PIN
|
||||
UART_RX_PIN = UART0_RX_PIN
|
||||
)
|
||||
|
||||
// I2C0 (Qwiic/STEMMA QT + RTC PCF85063A).
|
||||
const (
|
||||
I2C0_SDA_PIN = GPIO4
|
||||
I2C0_SCL_PIN = GPIO5
|
||||
)
|
||||
|
||||
// I2C1 is not routed to any header; NoPin satisfies the machine package.
|
||||
const (
|
||||
I2C1_SDA_PIN Pin = NoPin
|
||||
I2C1_SCL_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// Power / Sensing.
|
||||
const (
|
||||
VBUS_DETECT Pin = GPIO12
|
||||
RTC_ALARM Pin = GPIO13
|
||||
VBAT_SENSE Pin = GPIO26
|
||||
POWER_EN Pin = GPIO27
|
||||
SENSE_1V1 Pin = GPIO28
|
||||
|
||||
BATTERY = VBAT_SENSE
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
// USB identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Badger 2350"
|
||||
usb_STRING_MANUFACTURER = "Pimoroni"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2E8A
|
||||
usb_PID uint16 = 0x0005
|
||||
)
|
||||
@@ -0,0 +1,112 @@
|
||||
//go:build blinky2350
|
||||
|
||||
// Chip: RP2350A (QFN-60, 30 GPIO) -> target inherits from "rp2350".
|
||||
// Pin source: https://github.com/pimoroni/blinky2350/blob/main/board/pins.csv
|
||||
//
|
||||
// NOTES (verify before first flash):
|
||||
// - POWER_EN (GPIO27) likely switches peripheral power.
|
||||
// Verify polarity against the schematic; pull HIGH early if needed,
|
||||
// otherwise the display will remain blank.
|
||||
// - CHARGE_STAT is connected to EXT_GPIO2 (I2C IO expander) according to pins.csv,
|
||||
// NOT the RP2350 -> intentionally not defined as a pin here.
|
||||
// - The dot matrix is NOT driven via SPI (Latch/Blank/
|
||||
// Row-Clock are not SPI signals) -> use PIO or bit-banging.
|
||||
// SPI0 and SPI1 are therefore set to NoPin (required by the machine package).
|
||||
|
||||
package machine
|
||||
|
||||
// Crystal oscillator frequency.
|
||||
const xoscFreq = 12 // MHz
|
||||
|
||||
// User buttons.
|
||||
const (
|
||||
BUTTON_A Pin = GPIO7
|
||||
BUTTON_B Pin = GPIO9
|
||||
BUTTON_C Pin = GPIO10
|
||||
BUTTON_UP Pin = GPIO11
|
||||
BUTTON_DOWN Pin = GPIO6
|
||||
BUTTON_HOME Pin = GPIO22
|
||||
BUTTON_BOOT Pin = BUTTON_HOME
|
||||
|
||||
BUTTON_RESET Pin = GPIO14
|
||||
BUTTON_INT Pin = GPIO15
|
||||
)
|
||||
|
||||
// Case LEDs (4-zone mono illumination), named CL0..CL3 in the docs.
|
||||
const (
|
||||
CL0 Pin = GPIO0
|
||||
CL1 Pin = GPIO1
|
||||
CL2 Pin = GPIO2
|
||||
CL3 Pin = GPIO3
|
||||
|
||||
LED = CL0
|
||||
)
|
||||
|
||||
// LED dot matrix: column shift register + row driver.
|
||||
// NOT SPI -> drive via PIO or bit-banging.
|
||||
const (
|
||||
DISPLAY_COL_SCLK Pin = GPIO16
|
||||
DISPLAY_COL_DATA Pin = GPIO17
|
||||
DISPLAY_COL_LATCH Pin = GPIO18
|
||||
DISPLAY_COL_BLANK Pin = GPIO19
|
||||
DISPLAY_ROW_DATA Pin = GPIO20
|
||||
DISPLAY_ROW_CLK Pin = GPIO21
|
||||
)
|
||||
|
||||
// I2C0 (Qwiic/STEMMA QT + RTC).
|
||||
const (
|
||||
I2C0_SDA_PIN = GPIO4
|
||||
I2C0_SCL_PIN = GPIO5
|
||||
)
|
||||
|
||||
// I2C1 is not routed to any header; NoPin satisfies the machine package.
|
||||
const (
|
||||
I2C1_SDA_PIN Pin = NoPin
|
||||
I2C1_SCL_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// SPI pins - the LED matrix is driven via PIO/bit-banging, not SPI.
|
||||
// NoPin satisfies the machine package requirements.
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = NoPin
|
||||
SPI0_SDO_PIN Pin = NoPin
|
||||
SPI0_SDI_PIN Pin = NoPin
|
||||
|
||||
SPI1_SCK_PIN Pin = NoPin
|
||||
SPI1_SDO_PIN Pin = NoPin
|
||||
SPI1_SDI_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// UART pins - default RP2350 UART0 mapping.
|
||||
// Note: GPIO0/GPIO1 are also CL0/CL1 (case LEDs)
|
||||
// Do not use UART0 and LEDs simultaneously.
|
||||
const (
|
||||
UART0_TX_PIN = GPIO0
|
||||
UART0_RX_PIN = GPIO1
|
||||
UART_TX_PIN = UART0_TX_PIN
|
||||
UART_RX_PIN = UART0_RX_PIN
|
||||
)
|
||||
|
||||
// Power / sensing.
|
||||
const (
|
||||
VBUS_DETECT Pin = GPIO12
|
||||
RTC_ALARM Pin = GPIO13
|
||||
VBAT_SENSE Pin = GPIO26
|
||||
POWER_EN Pin = GPIO27
|
||||
SENSE_1V1 Pin = GPIO28
|
||||
|
||||
BATTERY = VBAT_SENSE
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
// USB identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Blinky 2350"
|
||||
usb_STRING_MANUFACTURER = "Pimoroni"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2E8A
|
||||
usb_PID uint16 = 0x0005
|
||||
)
|
||||
@@ -0,0 +1,144 @@
|
||||
//go:build esp32s3_box3
|
||||
|
||||
// This file contains the pin mappings for the ESP32-S3-BOX-3 board.
|
||||
//
|
||||
// ESP32-S3-BOX-3 is an AI voice development kit with 2.4" display.
|
||||
// - https://github.com/espressif/esp-box
|
||||
//
|
||||
// Based on ESP-BOX-3 BSP from Espressif.
|
||||
|
||||
package machine
|
||||
|
||||
// CPUFrequency returns the current CPU frequency for ESP32-S3-BOX-3.
|
||||
// Returns 240MHz (max speed for ESP32-S3 with PSRAM)
|
||||
func CPUFrequency() uint32 {
|
||||
return 240_000_000 // 240MHz
|
||||
}
|
||||
|
||||
const (
|
||||
// GPIO pins available on ESP32-S3-BOX-3
|
||||
// Based on ESP-BOX-3 BSP pin definitions
|
||||
IO0 = GPIO0 // Button Config
|
||||
IO1 = GPIO1 // Button Mute / Mute Status
|
||||
IO2 = GPIO2 // I2S MCLK
|
||||
IO3 = GPIO3 // LCD Touch Interrupt
|
||||
IO4 = GPIO4 // LCD DC
|
||||
IO5 = GPIO5 // LCD CS
|
||||
IO6 = GPIO6 // LCD DATA0 (MOSI)
|
||||
IO7 = GPIO7 // LCD PCLK (SCK)
|
||||
IO8 = GPIO8 // I2C SDA
|
||||
IO9 = GPIO9 // SD Card D0
|
||||
IO10 = GPIO10
|
||||
IO11 = GPIO11 // SD Card CLK
|
||||
IO12 = GPIO12 // SD Card D3
|
||||
IO13 = GPIO13 // SD Card D1
|
||||
IO14 = GPIO14 // SD Card CMD
|
||||
IO15 = GPIO15 // I2S DOUT (Speaker)
|
||||
IO16 = GPIO16 // I2S DSIN (Microphone)
|
||||
IO17 = GPIO17 // I2S SCLK
|
||||
IO18 = GPIO18 // I2C SCL
|
||||
IO19 = GPIO19 // USB_NEG
|
||||
IO20 = GPIO20 // USB_POS
|
||||
IO21 = GPIO21
|
||||
IO38 = GPIO38
|
||||
IO39 = GPIO39
|
||||
IO40 = GPIO40 // I2C Dock SCL
|
||||
IO41 = GPIO41 // I2C Dock SDA
|
||||
IO42 = GPIO42 // SD Card D2
|
||||
IO43 = GPIO43 // SD Card Power
|
||||
IO44 = GPIO44 // (Unused)
|
||||
IO45 = GPIO45 // I2S LCLK (LRCK)
|
||||
IO46 = GPIO46 // Power Amp Control
|
||||
IO47 = GPIO47 // LCD Backlight
|
||||
IO48 = GPIO48 // LCD Reset
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
// SPI1 - used for LCD
|
||||
SPI1_SCK_PIN = IO7 // LCD_PCLK
|
||||
SPI1_MOSI_PIN = IO6 // LCD_DATA0 (MOSI)
|
||||
SPI1_MISO_PIN = NoPin // Not used for LCD
|
||||
SPI1_CS_PIN = IO5 // LCD_CS
|
||||
|
||||
// SPI2 (not used on this board)
|
||||
SPI2_SCK_PIN = NoPin
|
||||
SPI2_MOSI_PIN = NoPin
|
||||
SPI2_MISO_PIN = NoPin
|
||||
SPI2_CS_PIN = NoPin
|
||||
)
|
||||
|
||||
// LCD pins (ST7789/ILI9341)
|
||||
const (
|
||||
LCD_SCK_PIN = SPI1_SCK_PIN
|
||||
LCD_SDO_PIN = SPI1_MOSI_PIN
|
||||
LCD_SDI_PIN = NoPin
|
||||
LCD_SS_PIN = SPI1_CS_PIN
|
||||
LCD_DC_PIN = IO4
|
||||
LCD_RST_PIN = IO48
|
||||
LCD_BL_PIN = IO47
|
||||
)
|
||||
|
||||
// I2C pins (Internal I2C for sensors)
|
||||
const (
|
||||
SDA0_PIN = IO8
|
||||
SCL0_PIN = IO18
|
||||
|
||||
SDA_PIN = SDA0_PIN
|
||||
SCL_PIN = SCL0_PIN
|
||||
)
|
||||
|
||||
// Dock I2C pins (for expansion dock)
|
||||
const (
|
||||
SDA1_PIN = IO41
|
||||
SCL1_PIN = IO40
|
||||
)
|
||||
|
||||
// UART pins (USB CDC - native USB on ESP32-S3)
|
||||
// Note: Native USB doesn't require GPIO pins, these are kept for compatibility
|
||||
const (
|
||||
UART_TX_PIN = NoPin
|
||||
UART_RX_PIN = NoPin
|
||||
)
|
||||
|
||||
// Built-in LED
|
||||
// Using LCD backlight as LED indicator
|
||||
const LED = GPIO47
|
||||
|
||||
// Buttons
|
||||
const (
|
||||
BUTTON_CONFIG = GPIO0
|
||||
BUTTON_MUTE = GPIO1
|
||||
)
|
||||
|
||||
// Audio pins (I2S)
|
||||
const (
|
||||
I2S_SCLK_PIN = IO17 // Bit Clock
|
||||
I2S_MCLK_PIN = IO2 // Master Clock
|
||||
I2S_LRCK_PIN = IO45 // Left/Right Clock (Frame Sync)
|
||||
I2S_DOUT_PIN = IO15 // Data Out to Speaker (ES8311)
|
||||
I2S_DSIN_PIN = IO16 // Data In from Microphone (ES7210)
|
||||
)
|
||||
|
||||
// SD Card pins (SD/MMC mode)
|
||||
const (
|
||||
SDCARD_CMD_PIN = IO14
|
||||
SDCARD_CLK_PIN = IO11
|
||||
SDCARD_D0_PIN = IO9
|
||||
SDCARD_D1_PIN = IO13
|
||||
SDCARD_D2_PIN = IO42
|
||||
SDCARD_D3_PIN = IO12
|
||||
SDCARD_PWR_PIN = IO43 // SD Card Power
|
||||
)
|
||||
|
||||
// USB pins (native USB)
|
||||
const (
|
||||
USB_DPPIN = IO20 // USB D+
|
||||
USB_DMPIN = IO19 // USB D-
|
||||
)
|
||||
|
||||
// Touch interrupt
|
||||
const TOUCH_INT_PIN = IO3
|
||||
|
||||
// Power amplifier control
|
||||
const POWER_AMP_PIN = IO46
|
||||
@@ -41,6 +41,40 @@ const (
|
||||
D29 Pin = GPIO29
|
||||
)
|
||||
|
||||
// GPIO pin aliases
|
||||
const (
|
||||
GP0 Pin = GPIO0
|
||||
GP1 Pin = GPIO1
|
||||
GP2 Pin = GPIO2
|
||||
GP3 Pin = GPIO3
|
||||
GP4 Pin = GPIO4
|
||||
GP5 Pin = GPIO5
|
||||
GP6 Pin = GPIO6
|
||||
GP7 Pin = GPIO7
|
||||
GP8 Pin = GPIO8
|
||||
GP9 Pin = GPIO9
|
||||
GP10 Pin = GPIO10
|
||||
GP11 Pin = GPIO11
|
||||
GP12 Pin = GPIO12
|
||||
GP13 Pin = GPIO13
|
||||
GP14 Pin = GPIO14
|
||||
GP15 Pin = GPIO15
|
||||
GP16 Pin = GPIO16
|
||||
GP17 Pin = GPIO17
|
||||
GP18 Pin = GPIO18
|
||||
GP19 Pin = GPIO19
|
||||
GP20 Pin = GPIO20
|
||||
GP21 Pin = GPIO21
|
||||
GP22 Pin = GPIO22
|
||||
GP23 Pin = GPIO23
|
||||
GP24 Pin = GPIO24
|
||||
GP25 Pin = GPIO25
|
||||
GP26 Pin = GPIO26
|
||||
GP27 Pin = GPIO27
|
||||
GP28 Pin = GPIO28
|
||||
GP29 Pin = GPIO29
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = D26
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//go:build xiao_esp32c6
|
||||
|
||||
// This file contains the pin mappings for the Seeed XIAO ESP32C6 board.
|
||||
//
|
||||
// Seeed Studio XIAO ESP32C6 is an IoT mini development board based on
|
||||
// the Espressif ESP32-C6 WiFi 6 / Bluetooth 5 / 802.15.4 chip.
|
||||
//
|
||||
// - https://www.seeedstudio.com/Seeed-Studio-XIAO-ESP32C6-p-5884.html
|
||||
// - https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
|
||||
|
||||
package machine
|
||||
|
||||
// Digital Pins
|
||||
const (
|
||||
D0 = GPIO0
|
||||
D1 = GPIO1
|
||||
D2 = GPIO2
|
||||
D3 = GPIO21
|
||||
D4 = GPIO22
|
||||
D5 = GPIO23
|
||||
D6 = GPIO16
|
||||
D7 = GPIO17
|
||||
D8 = GPIO19
|
||||
D9 = GPIO20
|
||||
D10 = GPIO18
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = GPIO0
|
||||
A1 = GPIO1
|
||||
A2 = GPIO2
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN = GPIO16
|
||||
UART_RX_PIN = GPIO17
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = GPIO22
|
||||
SCL_PIN = GPIO23
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI_SCK_PIN = GPIO19
|
||||
SPI_SDI_PIN = GPIO20
|
||||
SPI_SDO_PIN = GPIO18
|
||||
)
|
||||
|
||||
// Onboard LEDs
|
||||
const (
|
||||
LED = GPIO15
|
||||
)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build !baremetal || atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || rp2350 || mimxrt1062 || (esp32c3 && !m5stamp_c3) || esp32 || esp32s3
|
||||
//go:build !baremetal || atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || rp2350 || mimxrt1062 || (esp32c3 && !m5stamp_c3) || esp32 || esp32c6 || esp32s3
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -23,19 +23,6 @@ const (
|
||||
NumberOfUSBEndpoints = 8
|
||||
)
|
||||
|
||||
var (
|
||||
endPoints = []uint32{
|
||||
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
|
||||
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
|
||||
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
||||
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
||||
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
|
||||
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||
}
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
if dev.initcomplete {
|
||||
@@ -188,7 +175,7 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
for i = 1; i < NumberOfUSBEndpoints; i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
@@ -206,6 +193,8 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
// Note: Both IN (Bank 1) and OUT (Bank 0) configurations share the same EPCFG register.
|
||||
// We must use getEPCFG(ep) | ... to avoid clearing/disabling the opposite direction.
|
||||
switch config {
|
||||
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn:
|
||||
// set packet size
|
||||
@@ -215,7 +204,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_INTERRUPT+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
|
||||
@@ -227,7 +216,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_BULK+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
@@ -246,7 +235,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_INTERRUPT+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
@@ -265,7 +254,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_BULK+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
@@ -663,3 +652,19 @@ func setEPINTENSET(ep uint32, val uint8) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dev *USBDevice) SetStallEPIn(ep uint32) {
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_STALLRQ1)
|
||||
}
|
||||
|
||||
func (dev *USBDevice) SetStallEPOut(ep uint32) {
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_STALLRQ0)
|
||||
}
|
||||
|
||||
func (dev *USBDevice) ClearStallEPIn(ep uint32) {
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_STALLRQ1)
|
||||
}
|
||||
|
||||
func (dev *USBDevice) ClearStallEPOut(ep uint32) {
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_STALLRQ0)
|
||||
}
|
||||
|
||||
@@ -23,19 +23,6 @@ const (
|
||||
NumberOfUSBEndpoints = 8
|
||||
)
|
||||
|
||||
var (
|
||||
endPoints = []uint32{
|
||||
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
|
||||
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
|
||||
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
||||
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
||||
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
|
||||
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||
}
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
if dev.initcomplete {
|
||||
@@ -191,7 +178,7 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
for i = 1; i < NumberOfUSBEndpoints; i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
@@ -209,6 +196,8 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
// Note: Both IN (Bank 1) and OUT (Bank 0) configurations share the same EPCFG register.
|
||||
// We must use getEPCFG(ep) | ... to avoid clearing/disabling the opposite direction.
|
||||
switch config {
|
||||
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn:
|
||||
// set packet size
|
||||
@@ -218,7 +207,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_INTERRUPT+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
@@ -230,7 +219,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_BULK+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
@@ -249,7 +238,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_INTERRUPT+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
@@ -268,7 +257,7 @@ func initEndpoint(ep, config uint32) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb.ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb.ENDPOINT_TYPE_BULK+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
@@ -494,3 +483,19 @@ func setEPINTENCLR(ep uint32, val uint8) {
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
|
||||
func (dev *USBDevice) SetStallEPIn(ep uint32) {
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_STALLRQ1)
|
||||
}
|
||||
|
||||
func (dev *USBDevice) SetStallEPOut(ep uint32) {
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_STALLRQ0)
|
||||
}
|
||||
|
||||
func (dev *USBDevice) ClearStallEPIn(ep uint32) {
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_STALLRQ1)
|
||||
}
|
||||
|
||||
func (dev *USBDevice) ClearStallEPOut(ep uint32) {
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_STALLRQ0)
|
||||
}
|
||||
|
||||
@@ -295,16 +295,33 @@ var DefaultUART = UART0
|
||||
|
||||
var (
|
||||
UART0 = &_UART0
|
||||
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
||||
_UART0 = UART{
|
||||
Bus: esp.UART0,
|
||||
Buffer: NewRingBuffer(),
|
||||
TXRXSignal: 14,
|
||||
RTSCTSSignal: 15,
|
||||
}
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
||||
_UART1 = UART{
|
||||
Bus: esp.UART1,
|
||||
Buffer: NewRingBuffer(),
|
||||
TXRXSignal: 17,
|
||||
RTSCTSSignal: 18,
|
||||
}
|
||||
UART2 = &_UART2
|
||||
_UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()}
|
||||
_UART2 = UART{
|
||||
Bus: esp.UART2,
|
||||
Buffer: NewRingBuffer(),
|
||||
TXRXSignal: 198,
|
||||
RTSCTSSignal: 199,
|
||||
}
|
||||
)
|
||||
|
||||
type UART struct {
|
||||
Bus *esp.UART_Type
|
||||
Buffer *RingBuffer
|
||||
Bus *esp.UART_Type
|
||||
Buffer *RingBuffer
|
||||
TXRXSignal uint32
|
||||
RTSCTSSignal uint32
|
||||
}
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) {
|
||||
@@ -312,6 +329,22 @@ func (uart *UART) Configure(config UARTConfig) {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate)
|
||||
|
||||
if config.RX != NoPin {
|
||||
config.RX.configure(PinConfig{Mode: PinInputPullup}, uart.TXRXSignal)
|
||||
}
|
||||
|
||||
if config.TX != NoPin {
|
||||
config.TX.configure(PinConfig{Mode: PinOutput}, uart.TXRXSignal)
|
||||
}
|
||||
|
||||
if config.RTS != NoPin {
|
||||
config.RTS.configure(PinConfig{Mode: PinOutput}, uart.RTSCTSSignal)
|
||||
}
|
||||
|
||||
if config.CTS != NoPin {
|
||||
config.CTS.configure(PinConfig{Mode: PinInputPullup}, uart.RTSCTSSignal)
|
||||
}
|
||||
}
|
||||
|
||||
func (uart *UART) writeByte(b byte) error {
|
||||
@@ -319,6 +352,7 @@ func (uart *UART) writeByte(b byte) error {
|
||||
// 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).
|
||||
gosched()
|
||||
}
|
||||
// Write to the TX_FIFO register.
|
||||
(*volatile.Register8)(unsafe.Add(unsafe.Pointer(uart.Bus), 0x200C0000)).Set(b)
|
||||
|
||||
@@ -19,3 +19,10 @@ var (
|
||||
useExt1: false,
|
||||
}
|
||||
)
|
||||
|
||||
// enableI2C0PeriphClock enables the I2C0 peripheral clock via SYSTEM.
|
||||
func enableI2C0PeriphClock() {
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_I2C_EXT0_RST(1)
|
||||
esp.SYSTEM.SetPERIP_CLK_EN0_I2C_EXT0_CLK_EN(1)
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_I2C_EXT0_RST(0)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,546 @@
|
||||
//go:build esp32c6
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"device/riscv"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const deviceName = esp.Device
|
||||
const maxPin = 31
|
||||
const cpuInterruptFromPin = 6
|
||||
|
||||
// CPUFrequency returns the current CPU frequency of the chip.
|
||||
// Currently it is a fixed frequency but it may allow changing in the future.
|
||||
func CPUFrequency() uint32 {
|
||||
return 160e6 // 160MHz
|
||||
}
|
||||
|
||||
const (
|
||||
PinOutput PinMode = iota
|
||||
PinInput
|
||||
PinInputPullup
|
||||
PinInputPulldown
|
||||
PinAnalog
|
||||
)
|
||||
|
||||
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
|
||||
GPIO22 Pin = 22
|
||||
GPIO23 Pin = 23
|
||||
GPIO24 Pin = 24
|
||||
GPIO25 Pin = 25
|
||||
GPIO26 Pin = 26
|
||||
GPIO27 Pin = 27
|
||||
GPIO28 Pin = 28
|
||||
GPIO29 Pin = 29
|
||||
GPIO30 Pin = 30
|
||||
)
|
||||
|
||||
const (
|
||||
ADC0 Pin = GPIO0
|
||||
ADC1 Pin = GPIO1
|
||||
ADC2 Pin = GPIO2
|
||||
ADC3 Pin = GPIO3
|
||||
ADC4 Pin = GPIO4
|
||||
ADC5 Pin = GPIO5
|
||||
ADC6 Pin = GPIO6
|
||||
)
|
||||
|
||||
type PinChange uint8
|
||||
|
||||
// Pin change interrupt constants for SetInterrupt.
|
||||
const (
|
||||
PinRising PinChange = iota + 1
|
||||
PinFalling
|
||||
PinToggle
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if p == NoPin {
|
||||
// This simplifies pin configuration in peripherals such as SPI.
|
||||
return
|
||||
}
|
||||
|
||||
var muxConfig uint32
|
||||
|
||||
// Configure this pin as a GPIO pin.
|
||||
const function = 1 // function 1 is GPIO for every pin
|
||||
muxConfig |= function << esp.IO_MUX_GPIO_MCU_SEL_Pos
|
||||
|
||||
// FUN_IE: disable for PinAnalog (high-Z for ADC)
|
||||
if config.Mode != PinAnalog {
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
}
|
||||
|
||||
// Set drive strength: 0 is lowest, 3 is highest.
|
||||
muxConfig |= 2 << esp.IO_MUX_GPIO_FUN_DRV_Pos
|
||||
|
||||
// Select pull mode (no pulls for PinAnalog).
|
||||
if config.Mode == PinInputPullup {
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_WPU
|
||||
} else if config.Mode == PinInputPulldown {
|
||||
muxConfig |= esp.IO_MUX_GPIO_FUN_WPD
|
||||
}
|
||||
|
||||
// Configure the pad with the given IO mux configuration.
|
||||
p.mux().Set(muxConfig)
|
||||
|
||||
// Set the output signal to the simple GPIO output.
|
||||
p.outFunc().Set(0x80)
|
||||
|
||||
switch config.Mode {
|
||||
case PinOutput:
|
||||
// Set the 'output enable' bit.
|
||||
esp.GPIO.ENABLE_W1TS.Set(1 << p)
|
||||
case PinInput, PinInputPullup, PinInputPulldown, PinAnalog:
|
||||
// Clear the 'output enable' bit.
|
||||
esp.GPIO.ENABLE_W1TC.Set(1 << p)
|
||||
}
|
||||
}
|
||||
|
||||
// configure is the same as Configure, but allows setting a specific GPIO matrix signal.
|
||||
func (p Pin) configure(config PinConfig, signal uint32) {
|
||||
p.Configure(config)
|
||||
if signal == 256 {
|
||||
return
|
||||
}
|
||||
if config.Mode == PinOutput {
|
||||
p.outFunc().Set(signal)
|
||||
} else {
|
||||
inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p))
|
||||
}
|
||||
}
|
||||
|
||||
func initI2CExt1Clock() {}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
func (p Pin) pinReg() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.PIN0)) + 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))
|
||||
}
|
||||
|
||||
// mux returns the I/O mux configuration register corresponding to the given
|
||||
// GPIO pin.
|
||||
func (p Pin) mux() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.IO_MUX.GPIO0), uintptr(p)*4))
|
||||
}
|
||||
|
||||
// pin returns the PIN register corresponding to the given GPIO pin.
|
||||
func (p Pin) pin() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.PIN0), uintptr(p)*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)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin when configured as an input or as
|
||||
// an output.
|
||||
func (p Pin) Get() bool {
|
||||
reg := &esp.GPIO.IN
|
||||
return (reg.Get()>>p)&1 > 0
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return &esp.GPIO.OUT_W1TS, 1 << p
|
||||
}
|
||||
|
||||
func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
|
||||
return &esp.GPIO.OUT_W1TC, 1 << p
|
||||
}
|
||||
|
||||
// SetInterrupt sets an interrupt to be executed when a particular pin changes
|
||||
// state. The pin should already be configured as an input, including a pull up
|
||||
// or down if no external pull is provided.
|
||||
//
|
||||
// You can pass a nil func to unset the pin change interrupt. If you do so,
|
||||
// the change parameter is ignored and can be set to any value (such as 0).
|
||||
// If the pin is already configured with a callback, you must first unset
|
||||
// this pins interrupt before you can set a new callback.
|
||||
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
|
||||
if p >= maxPin {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
|
||||
if callback == nil {
|
||||
// Disable this pin interrupt
|
||||
p.pin().ClearBits(esp.GPIO_PIN_INT_TYPE_Msk | esp.GPIO_PIN_INT_ENA_Msk)
|
||||
|
||||
if pinCallbacks[p] != nil {
|
||||
pinCallbacks[p] = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if pinCallbacks[p] != nil {
|
||||
// The pin was already configured.
|
||||
// To properly re-configure a pin, unset it first and set a new
|
||||
// configuration.
|
||||
return ErrNoPinChangeChannel
|
||||
}
|
||||
pinCallbacks[p] = callback
|
||||
|
||||
onceSetupPinInterrupt.Do(func() {
|
||||
err = setupPinInterrupt()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.pin().Set(
|
||||
(p.pin().Get() & ^uint32(esp.GPIO_PIN_INT_TYPE_Msk|esp.GPIO_PIN_INT_ENA_Msk)) |
|
||||
uint32(change)<<esp.GPIO_PIN_INT_TYPE_Pos | uint32(1)<<esp.GPIO_PIN_INT_ENA_Pos)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
pinCallbacks [maxPin]func(Pin)
|
||||
onceSetupPinInterrupt sync.Once
|
||||
)
|
||||
|
||||
func setupPinInterrupt() error {
|
||||
esp.INTERRUPT_CORE0.GPIO_INTERRUPT_PRO_MAP.Set(cpuInterruptFromPin)
|
||||
return interrupt.New(cpuInterruptFromPin, func(interrupt.Interrupt) {
|
||||
status := esp.GPIO.STATUS.Get()
|
||||
// Clear before processing so new edges during callbacks are not lost.
|
||||
esp.GPIO.STATUS_W1TC.Set(status)
|
||||
for i, mask := 0, uint32(1); i < maxPin; i, mask = i+1, mask<<1 {
|
||||
if (status&mask) != 0 && pinCallbacks[i] != nil {
|
||||
pinCallbacks[i](Pin(i))
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultUART = UART0
|
||||
|
||||
UART0 = &_UART0
|
||||
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
||||
|
||||
onceUart = sync.Once{}
|
||||
errSamePins = errors.New("UART: invalid pin combination")
|
||||
errWrongUART = errors.New("UART: unsupported UARTn")
|
||||
errWrongBitSize = errors.New("UART: invalid data size")
|
||||
errWrongStopBitSize = errors.New("UART: invalid bit size")
|
||||
)
|
||||
|
||||
type UART struct {
|
||||
Bus *esp.UART_Type
|
||||
Buffer *RingBuffer
|
||||
ParityErrorDetected bool // set when parity error detected
|
||||
DataErrorDetected bool // set when data corruption detected
|
||||
DataOverflowDetected bool // set when data overflow detected in UART FIFO buffer or RingBuffer
|
||||
}
|
||||
|
||||
const (
|
||||
defaultDataBits = 8
|
||||
defaultStopBit = 1
|
||||
defaultParity = ParityNone
|
||||
|
||||
uartInterrupts = esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA |
|
||||
esp.UART_INT_ENA_PARITY_ERR_INT_ENA |
|
||||
esp.UART_INT_ENA_FRM_ERR_INT_ENA |
|
||||
esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA |
|
||||
esp.UART_INT_ENA_GLITCH_DET_INT_ENA
|
||||
|
||||
pplClockFreq = 80e6
|
||||
)
|
||||
|
||||
type registerSet struct {
|
||||
interruptMapReg *volatile.Register32
|
||||
gpioMatrixSignal uint32
|
||||
}
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) error {
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
if config.TX == config.RX {
|
||||
return errSamePins
|
||||
}
|
||||
switch {
|
||||
case uart.Bus == esp.UART0:
|
||||
return uart.configure(config, registerSet{
|
||||
interruptMapReg: &esp.INTERRUPT_CORE0.UART0_INTR_MAP,
|
||||
gpioMatrixSignal: 6,
|
||||
})
|
||||
case uart.Bus == esp.UART1:
|
||||
return uart.configure(config, registerSet{
|
||||
interruptMapReg: &esp.INTERRUPT_CORE0.UART1_INTR_MAP,
|
||||
gpioMatrixSignal: 9,
|
||||
})
|
||||
}
|
||||
return errWrongUART
|
||||
}
|
||||
|
||||
func (uart *UART) configure(config UARTConfig, regs registerSet) error {
|
||||
initUARTClock(uart.Bus)
|
||||
|
||||
// - disable TX/RX clock to make sure the UART transmitter or receiver is not at work during configuration
|
||||
uart.Bus.SetCLK_CONF_TX_SCLK_EN(0)
|
||||
uart.Bus.SetCLK_CONF_RX_SCLK_EN(0)
|
||||
|
||||
// - default clock source: 1=XTAL_CLK, 2=RC_FAST_CLK, 3=FOSC_CLK
|
||||
// On C6, SCLK_SEL=1 selects XTAL (40 MHz). Use FOSC (80 MHz) for
|
||||
// better baud-rate accuracy.
|
||||
uart.Bus.SetCLK_CONF_SCLK_SEL(3) // FOSC_CLK (80 MHz)
|
||||
// reset divisor of the divider via UART_SCLK_DIV_NUM, UART_SCLK_DIV_A, and UART_SCLK_DIV_B
|
||||
uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(0)
|
||||
uart.Bus.SetCLK_CONF_SCLK_DIV_A(0)
|
||||
uart.Bus.SetCLK_CONF_SCLK_DIV_B(0)
|
||||
|
||||
uart.SetBaudRate(config.BaudRate)
|
||||
uart.SetFormat(defaultDataBits, defaultStopBit, defaultParity)
|
||||
|
||||
// - set UART mode
|
||||
uart.Bus.SetRS485_CONF_RS485_EN(0)
|
||||
uart.Bus.SetRS485_CONF_RS485TX_RX_EN(0)
|
||||
uart.Bus.SetRS485_CONF_RS485RXBY_TX_EN(0)
|
||||
uart.Bus.SetCONF0_IRDA_EN(0)
|
||||
// - disable hw-flow control
|
||||
uart.Bus.SetCONF0_TX_FLOW_EN(0)
|
||||
|
||||
// synchronize values into Core Clock
|
||||
uart.Bus.SetREG_UPDATE(1)
|
||||
|
||||
uart.setupPins(config, regs)
|
||||
uart.configureInterrupt(regs.interruptMapReg)
|
||||
uart.enableTransmitter()
|
||||
uart.enableReceiver()
|
||||
|
||||
// Start TX/RX
|
||||
uart.Bus.SetCLK_CONF_TX_SCLK_EN(1)
|
||||
uart.Bus.SetCLK_CONF_RX_SCLK_EN(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) SetFormat(dataBits, stopBits int, parity UARTParity) error {
|
||||
if dataBits < 5 {
|
||||
return errWrongBitSize
|
||||
}
|
||||
if stopBits > 1 {
|
||||
return errWrongStopBitSize
|
||||
}
|
||||
// - data length
|
||||
uart.Bus.SetCONF0_BIT_NUM(uint32(dataBits - 5))
|
||||
// - stop bit
|
||||
uart.Bus.SetCONF0_STOP_BIT_NUM(uint32(stopBits))
|
||||
// - parity check
|
||||
switch parity {
|
||||
case ParityNone:
|
||||
uart.Bus.SetCONF0_PARITY_EN(0)
|
||||
case ParityEven:
|
||||
uart.Bus.SetCONF0_PARITY_EN(1)
|
||||
uart.Bus.SetCONF0_PARITY(0)
|
||||
case ParityOdd:
|
||||
uart.Bus.SetCONF0_PARITY_EN(1)
|
||||
uart.Bus.SetCONF0_PARITY(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initUARTClock(bus *esp.UART_Type) {
|
||||
// On ESP32-C6, UART clock is controlled via PCR (Peripheral Clock Reset).
|
||||
switch bus {
|
||||
case esp.UART0:
|
||||
esp.PCR.SetUART0_CONF_UART0_CLK_EN(1)
|
||||
esp.PCR.SetUART0_CONF_UART0_RST_EN(0) // ensure not in reset
|
||||
bus.SetCLK_CONF_RST_CORE(1)
|
||||
esp.PCR.SetUART0_CONF_UART0_RST_EN(1)
|
||||
esp.PCR.SetUART0_CONF_UART0_RST_EN(0)
|
||||
bus.SetCLK_CONF_RST_CORE(0)
|
||||
// Configure SCLK divisor in PCR
|
||||
esp.PCR.SetUART0_SCLK_CONF_UART0_SCLK_DIV_NUM(0)
|
||||
esp.PCR.SetUART0_SCLK_CONF_UART0_SCLK_DIV_A(0)
|
||||
esp.PCR.SetUART0_SCLK_CONF_UART0_SCLK_DIV_B(0)
|
||||
case esp.UART1:
|
||||
esp.PCR.SetUART1_CONF_UART1_CLK_EN(1)
|
||||
esp.PCR.SetUART1_CONF_UART1_RST_EN(0)
|
||||
bus.SetCLK_CONF_RST_CORE(1)
|
||||
esp.PCR.SetUART1_CONF_UART1_RST_EN(1)
|
||||
esp.PCR.SetUART1_CONF_UART1_RST_EN(0)
|
||||
bus.SetCLK_CONF_RST_CORE(0)
|
||||
esp.PCR.SetUART1_SCLK_CONF_UART1_SCLK_DIV_NUM(0)
|
||||
esp.PCR.SetUART1_SCLK_CONF_UART1_SCLK_DIV_A(0)
|
||||
esp.PCR.SetUART1_SCLK_CONF_UART1_SCLK_DIV_B(0)
|
||||
}
|
||||
// wait for Core Clock to ready for configuration
|
||||
for bus.GetREG_UPDATE() > 0 {
|
||||
riscv.Asm("nop")
|
||||
}
|
||||
}
|
||||
|
||||
func (uart *UART) SetBaudRate(baudRate uint32) {
|
||||
// based on esp-idf
|
||||
max_div := uint32((1 << 12) - 1)
|
||||
sclk_div := (pplClockFreq + (max_div * baudRate) - 1) / (max_div * baudRate)
|
||||
clk_div := (pplClockFreq << 4) / (baudRate * sclk_div)
|
||||
uart.Bus.SetCLKDIV(clk_div >> 4)
|
||||
uart.Bus.SetCLKDIV_FRAG(clk_div & 0xf)
|
||||
// On C6, the SCLK divisor is in the PCR register.
|
||||
switch uart.Bus {
|
||||
case esp.UART0:
|
||||
esp.PCR.SetUART0_SCLK_CONF_UART0_SCLK_DIV_NUM(sclk_div - 1)
|
||||
case esp.UART1:
|
||||
esp.PCR.SetUART1_SCLK_CONF_UART1_SCLK_DIV_NUM(sclk_div - 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (uart *UART) setupPins(config UARTConfig, regs registerSet) {
|
||||
config.RX.Configure(PinConfig{Mode: PinInputPullup})
|
||||
config.TX.Configure(PinConfig{Mode: PinInputPullup})
|
||||
|
||||
// link TX with GPIO signal X (technical reference manual, GPIO matrix)
|
||||
config.TX.outFunc().Set(regs.gpioMatrixSignal)
|
||||
// link RX with GPIO signal X and route signals via GPIO matrix
|
||||
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX))
|
||||
}
|
||||
|
||||
func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) {
|
||||
// Disable all UART interrupts
|
||||
uart.Bus.INT_ENA.ClearBits(0x0ffff)
|
||||
|
||||
intrMapReg.Set(7)
|
||||
onceUart.Do(func() {
|
||||
_ = interrupt.New(7, func(i interrupt.Interrupt) {
|
||||
UART0.serveInterrupt(0)
|
||||
UART1.serveInterrupt(1)
|
||||
}).Enable()
|
||||
})
|
||||
}
|
||||
|
||||
func (uart *UART) serveInterrupt(num int) {
|
||||
// get interrupt status
|
||||
interrutFlag := uart.Bus.INT_ST.Get()
|
||||
if (interrutFlag & uartInterrupts) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// block UART interrupts while processing
|
||||
uart.Bus.INT_ENA.ClearBits(uartInterrupts)
|
||||
|
||||
if interrutFlag&esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA > 0 {
|
||||
for uart.Bus.GetSTATUS_RXFIFO_CNT() > 0 {
|
||||
b := uart.Bus.GetFIFO_RXFIFO_RD_BYTE()
|
||||
if !uart.Buffer.Put(byte(b & 0xff)) {
|
||||
uart.DataOverflowDetected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if interrutFlag&esp.UART_INT_ENA_PARITY_ERR_INT_ENA > 0 {
|
||||
uart.ParityErrorDetected = true
|
||||
}
|
||||
if 0 != interrutFlag&esp.UART_INT_ENA_FRM_ERR_INT_ENA {
|
||||
uart.DataErrorDetected = true
|
||||
}
|
||||
if 0 != interrutFlag&esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA {
|
||||
uart.DataOverflowDetected = true
|
||||
}
|
||||
if 0 != interrutFlag&esp.UART_INT_ENA_GLITCH_DET_INT_ENA {
|
||||
uart.DataErrorDetected = true
|
||||
}
|
||||
|
||||
// Clear the UART interrupt status
|
||||
uart.Bus.INT_CLR.SetBits(interrutFlag)
|
||||
uart.Bus.INT_CLR.ClearBits(interrutFlag)
|
||||
// Enable interrupts
|
||||
uart.Bus.INT_ENA.Set(uartInterrupts)
|
||||
}
|
||||
|
||||
const uart_empty_thresh_default = 10
|
||||
|
||||
func (uart *UART) enableTransmitter() {
|
||||
uart.Bus.SetCONF0_TXFIFO_RST(1)
|
||||
uart.Bus.SetCONF0_TXFIFO_RST(0)
|
||||
uart.Bus.SetCONF1_TXFIFO_EMPTY_THRHD(uart_empty_thresh_default)
|
||||
}
|
||||
|
||||
func (uart *UART) enableReceiver() {
|
||||
uart.Bus.SetCONF0_RXFIFO_RST(1)
|
||||
uart.Bus.SetCONF0_RXFIFO_RST(0)
|
||||
uart.Bus.SetCONF1_RXFIFO_FULL_THRHD(1)
|
||||
uart.Bus.SetINT_ENA_RXFIFO_FULL_INT_ENA(1)
|
||||
uart.Bus.SetINT_ENA_FRM_ERR_INT_ENA(1)
|
||||
uart.Bus.SetINT_ENA_PARITY_ERR_INT_ENA(1)
|
||||
uart.Bus.SetINT_ENA_GLITCH_DET_INT_ENA(1)
|
||||
uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1)
|
||||
}
|
||||
|
||||
func (uart *UART) writeByte(b byte) error {
|
||||
for (uart.Bus.STATUS.Get()&esp.UART_STATUS_TXFIFO_CNT_Msk)>>esp.UART_STATUS_TXFIFO_CNT_Pos >= 128 {
|
||||
// Wait until there is space in the transmit buffer.
|
||||
}
|
||||
uart.Bus.FIFO.Set(uint32(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user