mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-31 17:17:47 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b65447c7d5 | |||
| 0abc909739 | |||
| d984b55311 | |||
| 446fe1f5b3 | |||
| 873412b43a | |||
| 8a5ab5ab12 | |||
| 77cf60ef30 | |||
| a4e2e09457 | |||
| 4262f0ff1f | |||
| 1ceb63d14c | |||
| e1052f921c | |||
| 6dff85c756 | |||
| afae6b3795 | |||
| 2825b4fe74 | |||
| b5c5d95b68 | |||
| 633fe95187 | |||
| 64d7f1e436 | |||
| 9af535bf98 | |||
| 159f0051bb | |||
| 49e22fe678 | |||
| c119721e3b | |||
| 9294141d70 | |||
| bcf58c0840 | |||
| 7a61cb1bc3 | |||
| 11e1b2148f | |||
| a4599ba539 | |||
| 4c64784724 | |||
| 8d6b210c09 | |||
| 79ba6a50c3 | |||
| 2fb5174910 | |||
| b31281a5b6 | |||
| 8754f64f3b | |||
| caf405b01d | |||
| bb65c5ce2b | |||
| 283fed16a5 | |||
| 76bba13963 | |||
| ada11090a2 | |||
| a07287d3c6 | |||
| 1b2e764835 | |||
| 2c93a4085c | |||
| 906757603d | |||
| 5d16811199 | |||
| f2e576decf | |||
| 2d61972475 | |||
| 5c488e3145 | |||
| e45ff9c0e8 | |||
| 39805bca45 | |||
| 97842b367c | |||
| 9246899b30 | |||
| 04ace4de5f | |||
| 87a4676137 | |||
| fd20f63ee3 | |||
| 9dd249a431 | |||
| 777d3f3ea5 | |||
| ea3b5dc689 | |||
| 3dd502a928 | |||
| 48242ba8d6 | |||
| 7ea9eff406 | |||
| 80d94115dc | |||
| 9de76fb42e | |||
| 52c61de19f | |||
| f308d7d28c | |||
| 7729a36782 | |||
| 5404c81ffd | |||
| b52310fed2 | |||
| 046070074d | |||
| c23a5b65ef | |||
| b8e433821a | |||
| dd1a836903 | |||
| 4ed0936c0e | |||
| 7a5d4c9537 | |||
| af6bbaf532 | |||
| 4c7449efe5 | |||
| 995e815b63 | |||
| 109b5298c4 | |||
| a94e03eff2 | |||
| eb3d6261b4 | |||
| 15724848f9 | |||
| 8f40b107d9 | |||
| fa673f0827 | |||
| e3fe6d8f37 | |||
| 5c23f6fb6c | |||
| 5afb63df60 | |||
| 1d2c39c3b9 | |||
| 270a2f51fd | |||
| e87cd23e87 | |||
| db389ba443 | |||
| d1dfa1155c |
+37
-22
@@ -22,12 +22,12 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-14-v1
|
||||
- llvm-source-14-v3
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-14-v1
|
||||
key: llvm-source-14-v3
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
@@ -45,29 +45,32 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-linux-v1
|
||||
- binaryen-linux-v2
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
make binaryen
|
||||
- save_cache:
|
||||
key: binaryen-linux-v1
|
||||
key: binaryen-linux-v2
|
||||
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/buster/ llvm-toolchain-buster-<<parameters.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 \
|
||||
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<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 \
|
||||
@@ -80,43 +83,55 @@ commands:
|
||||
- build-binaryen-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v3-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-systemclang-v5
|
||||
- wasi-libc-sysroot-systemclang-v6
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v5
|
||||
key: wasi-libc-sysroot-systemclang-v6
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- 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
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest XTENSA=0
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
- run: make fmt-check
|
||||
|
||||
jobs:
|
||||
test-llvm11-go115:
|
||||
test-llvm13-go116:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-buster
|
||||
- image: golang:1.16-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
test-llvm12-go117:
|
||||
llvm: "13"
|
||||
test-llvm14-go119:
|
||||
docker:
|
||||
- image: circleci/golang:1.17-buster
|
||||
- image: golang:1.19beta1-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "12"
|
||||
llvm: "14"
|
||||
fmt-check: false
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-llvm11-go115
|
||||
- test-llvm12-go117
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
- test-llvm13-go116
|
||||
# This tests a beta version of Go. It should be removed once regular
|
||||
# release builds are built using this version.
|
||||
- test-llvm14-go119
|
||||
|
||||
@@ -7,6 +7,10 @@ on:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
name: build-macos
|
||||
@@ -67,7 +71,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v3
|
||||
key: wasi-libc-sysroot-v4
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -7,6 +7,10 @@ on:
|
||||
push:
|
||||
branches: [ dev, fix-docker-llvm-build ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: build-push-dev
|
||||
|
||||
+113
-7
@@ -7,6 +7,10 @@ on:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
# Build Linux binaries, ready for release.
|
||||
@@ -38,7 +42,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-v1
|
||||
key: llvm-source-14-linux-v2
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -77,7 +81,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v4
|
||||
key: wasi-libc-sysroot-linux-asserts-v5
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -163,7 +167,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||
@@ -179,7 +183,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-asserts-v1
|
||||
key: llvm-source-14-linux-asserts-v2
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -218,7 +222,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v4
|
||||
key: wasi-libc-sysroot-linux-asserts-v5
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -257,6 +261,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-user \
|
||||
g++-arm-linux-gnueabihf \
|
||||
@@ -264,7 +269,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.17'
|
||||
go-version: '1.18.1'
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@@ -276,7 +281,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-v1
|
||||
key: llvm-source-14-linux-v2
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -346,3 +351,104 @@ jobs:
|
||||
path: |
|
||||
/tmp/tinygo.linux-arm.tar.gz
|
||||
/tmp/tinygo_armhf.deb
|
||||
build-linux-arm64:
|
||||
# Build ARM64 Linux binaries, ready for release.
|
||||
# It is set to "needs: build-linux" because it modifies the release created
|
||||
# in that process to avoid doing lots of duplicate work and to avoid
|
||||
# complications around precompiled libraries such as compiler-rt shipped as
|
||||
# part of the release tarball.
|
||||
runs-on: ubuntu-18.04
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-user \
|
||||
g++-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-linux-arm64-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build CROSS=aarch64-linux-gnu
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v2
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-arm64-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git submodule update --init lib/binaryen
|
||||
make CROSS=aarch64-linux-gnu binaryen
|
||||
- name: Install fpm
|
||||
run: |
|
||||
sudo gem install --no-document fpm
|
||||
- name: Build TinyGo binary
|
||||
run: |
|
||||
make CROSS=aarch64-linux-gnu
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||
- name: Modify release
|
||||
run: |
|
||||
cp -p build/tinygo build/release/tinygo/bin
|
||||
cp -p build/wasm-opt build/release/tinygo/bin
|
||||
- name: Create arm64 release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=arm64
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_arm64.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: linux-arm64-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-arm64.tar.gz
|
||||
/tmp/tinygo_arm64.deb
|
||||
|
||||
@@ -7,6 +7,10 @@ on:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-2022
|
||||
@@ -16,6 +20,8 @@ jobs:
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -35,7 +41,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-windows-v1
|
||||
key: llvm-source-14-windows-v2
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -49,7 +55,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-windows-v1
|
||||
key: llvm-build-14-windows-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -59,14 +65,14 @@ jobs:
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build
|
||||
make llvm-build CCACHE=OFF
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Cache wasi-libc sysroot
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v3
|
||||
key: wasi-libc-sysroot-v4
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -78,6 +84,7 @@ jobs:
|
||||
shell: bash
|
||||
run: make test GOTESTFLAGS="-v -short"
|
||||
- name: Build TinyGo release tarball
|
||||
shell: bash
|
||||
run: make build/release -j4
|
||||
- name: Make release artifact
|
||||
shell: bash
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
|
||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
|
||||
* Go (1.15+)
|
||||
* Go (1.16+)
|
||||
* Standard build tools (gcc/clang)
|
||||
* git
|
||||
* CMake
|
||||
|
||||
@@ -1,3 +1,55 @@
|
||||
0.24.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- remove support for go 1.15
|
||||
- remove support for LLVM 11 and LLVM 12
|
||||
- add initial Go 1.19 beta support
|
||||
- `test`: fix package/... syntax
|
||||
* **compiler**
|
||||
- add support for the embed package
|
||||
- `builder`: improve error message for "command not found"
|
||||
- `builder`: add support for ThinLTO on MacOS and Windows
|
||||
- `builder`: free LLVM objects after use, to reduce memory leaking
|
||||
- `builder`: improve `-no-debug` error messages
|
||||
- `cgo`: be more strict: CGo now requires every Go file to import the headers it needs
|
||||
- `compiler`: alignof(func) is 1 pointer, not 2
|
||||
- `compiler`: add support for type parameters (aka generics)
|
||||
- `compiler`: implement `recover()` built-in function
|
||||
- `compiler`: support atomic, volatile, and LLVM memcpy-like functions in defer
|
||||
- `compiler`: drop support for macos syscalls via inline assembly
|
||||
- `interp`: do not try to interpret past task.Pause()
|
||||
- `interp`: fix some buggy localValue handling
|
||||
- `interp`: do not unroll loops
|
||||
- `transform`: fix MakeGCStackSlots that caused a possible GC bug on WebAssembly
|
||||
* **standard library**
|
||||
- `os`: enable os.Stdin for baremetal target
|
||||
- `reflect`: add `Value.UnsafePointer` method
|
||||
- `runtime`: scan GC globals conservatively on Windows, MacOS, Linux and Nintendo Switch
|
||||
- `runtime`: add per-map hash seeds
|
||||
- `runtime`: handle nil map write panics
|
||||
- `runtime`: add stronger hash functions
|
||||
- `syscall`: implement `Getpagesize`
|
||||
* **targets**
|
||||
- `atmega2560`: support UART1-3 + example for uart
|
||||
- `avr`: use compiler-rt for improved float64 support
|
||||
- `avr`: simplify timer-based time
|
||||
- `avr`: fix race condition in stack write
|
||||
- `darwin`: add support for `GOARCH=arm64` (aka Apple Silicon)
|
||||
- `darwin`: support `-size=short` and `-size=full` flag
|
||||
- `rp2040`: replace sleep 'busy loop' with timer alarm
|
||||
- `rp2040`: align api for `PortMaskSet`, `PortMaskClear`
|
||||
- `rp2040`: fix GPIO interrupts
|
||||
- `samd21`, `samd51`, `nrf52840`: add support for USBHID (keyboard / mouse)
|
||||
- `wasm`: update wasi-libc version
|
||||
- `wasm`: use newer WebAssembly features
|
||||
* **boards**
|
||||
- add Badger 2040
|
||||
- `matrixportal-m4`: attach USB DP to the correct pin
|
||||
- `teensy40`: add I2C support
|
||||
- `wioterminal`: fix I2C definition
|
||||
|
||||
|
||||
0.23.0
|
||||
---
|
||||
|
||||
|
||||
@@ -38,10 +38,16 @@ MD5SUM = md5sum
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
||||
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=ON'
|
||||
# Check for ccache if the user hasn't set it to on or off.
|
||||
ifeq (, $(CCACHE))
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||
CCACHE := ON
|
||||
else
|
||||
CCACHE := OFF
|
||||
endif
|
||||
endif
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
||||
|
||||
# Allow enabling LLVM assertions
|
||||
ifeq (1, $(ASSERT))
|
||||
@@ -68,6 +74,15 @@ ifneq ($(CROSS),)
|
||||
-DLLVM_TARGET_ARCH=ARM
|
||||
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else ifeq ($(CROSS), aarch64-linux-gnu)
|
||||
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
|
||||
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DLLVM_TARGET_ARCH=AArch64
|
||||
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else
|
||||
$(error Unknown cross compilation target: $(CROSS))
|
||||
endif
|
||||
@@ -141,7 +156,7 @@ endif
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
|
||||
FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform
|
||||
fmt:
|
||||
@gofmt -l -w $(FMT_PATHS)
|
||||
fmt-check:
|
||||
@@ -203,7 +218,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||
$(LLVM_BUILDDIR)/build.ninja:
|
||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
|
||||
# Build LLVM.
|
||||
@@ -225,7 +240,7 @@ endif
|
||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG" MALLOC_IMPL=none WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
||||
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||
|
||||
|
||||
# Build the Go compiler.
|
||||
@@ -256,6 +271,7 @@ TEST_PACKAGES_FAST = \
|
||||
crypto/sha256 \
|
||||
crypto/sha512 \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
encoding/base32 \
|
||||
@@ -297,10 +313,14 @@ TEST_PACKAGES_LINUX := \
|
||||
archive/zip \
|
||||
compress/flate \
|
||||
compress/lzw \
|
||||
crypto/hmac \
|
||||
debug/dwarf \
|
||||
debug/plan9obj \
|
||||
io/fs \
|
||||
testing/fstest
|
||||
io/ioutil \
|
||||
strconv \
|
||||
testing/fstest \
|
||||
text/template/parse
|
||||
|
||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||
|
||||
@@ -366,6 +386,8 @@ tinygo-baremetal:
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
$(TINYGO) version
|
||||
# regression test for #2892
|
||||
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
||||
# compile-only platform-independent examples
|
||||
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
|
||||
# regression test for #2563
|
||||
@@ -385,6 +407,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||
@@ -401,6 +425,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@@ -535,6 +563,10 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -542,6 +574,11 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
# test usbhid
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -633,7 +670,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo
|
||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
ifneq ($(OS),Windows_NT)
|
||||
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||
# not garbage collected.
|
||||
@@ -709,12 +747,12 @@ endif
|
||||
@cp -rp llvm-project/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
|
||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||
|
||||
release:
|
||||
tar -czf build/release.tar.gz -C build/release tinygo
|
||||
|
||||
+252
-93
@@ -4,6 +4,7 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
@@ -80,6 +81,7 @@ type packageAction struct {
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
OptLevel int // LLVM optimization level (0-3)
|
||||
SizeLevel int // LLVM optimization for size level (0-2)
|
||||
@@ -188,9 +190,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
||||
Sizes: compiler.Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -208,8 +211,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Add jobs to compile each package.
|
||||
// Packages that have a cache hit will not be compiled again.
|
||||
var packageJobs []*compileJob
|
||||
packageBitcodePaths := make(map[string]string)
|
||||
packageActionIDs := make(map[string]string)
|
||||
packageActionIDJobs := make(map[string]*compileJob)
|
||||
|
||||
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
||||
version := goenv.Version
|
||||
@@ -225,6 +227,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
||||
}
|
||||
|
||||
var embedFileObjects []*compileJob
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg // necessary to avoid a race condition
|
||||
|
||||
@@ -234,52 +237,114 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
}
|
||||
sort.Strings(undefinedGlobals)
|
||||
|
||||
// Create a cache key: a hash from the action ID below that contains all
|
||||
// the parameters for the build.
|
||||
actionID := packageAction{
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerBuildID: string(compilerBuildID),
|
||||
TinyGoVersion: goenv.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
OptLevel: optLevel,
|
||||
SizeLevel: sizeLevel,
|
||||
UndefinedGlobals: undefinedGlobals,
|
||||
// Make compile jobs to load files to be embedded in the output binary.
|
||||
var actionIDDependencies []*compileJob
|
||||
allFiles := map[string][]*loader.EmbedFile{}
|
||||
for _, files := range pkg.EmbedGlobals {
|
||||
for _, file := range files {
|
||||
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||
}
|
||||
}
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
for name, files := range allFiles {
|
||||
name := name
|
||||
files := files
|
||||
job := &compileJob{
|
||||
description: "make object file for " + name,
|
||||
run: func(job *compileJob) error {
|
||||
// Read the file contents in memory.
|
||||
path := filepath.Join(pkg.Dir, name)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hash the file.
|
||||
sum := sha256.Sum256(data)
|
||||
hexSum := hex.EncodeToString(sum[:16])
|
||||
|
||||
for _, file := range files {
|
||||
file.Size = uint64(len(data))
|
||||
file.Hash = hexSum
|
||||
if file.NeedsData {
|
||||
file.Data = data
|
||||
}
|
||||
}
|
||||
|
||||
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
|
||||
return err
|
||||
},
|
||||
}
|
||||
actionIDDependencies = append(actionIDDependencies, job)
|
||||
embedFileObjects = append(embedFileObjects, job)
|
||||
}
|
||||
|
||||
// Action ID jobs need to know the action ID of all the jobs the package
|
||||
// imports.
|
||||
var importedPackages []*compileJob
|
||||
for _, imported := range pkg.Pkg.Imports() {
|
||||
hash, ok := packageActionIDs[imported.Path()]
|
||||
job, ok := packageActionIDJobs[imported.Path()]
|
||||
if !ok {
|
||||
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||
}
|
||||
actionID.Imports[imported.Path()] = hash
|
||||
importedPackages = append(importedPackages, job)
|
||||
actionIDDependencies = append(actionIDDependencies, job)
|
||||
}
|
||||
buf, err := json.Marshal(actionID)
|
||||
if err != nil {
|
||||
panic(err) // shouldn't happen
|
||||
|
||||
// Create a job that will calculate the action ID for a package compile
|
||||
// job. The action ID is the cache key that is used for caching this
|
||||
// package.
|
||||
packageActionIDJob := &compileJob{
|
||||
description: "calculate cache key for package " + pkg.ImportPath,
|
||||
dependencies: actionIDDependencies,
|
||||
run: func(job *compileJob) error {
|
||||
// Create a cache key: a hash from the action ID below that contains all
|
||||
// the parameters for the build.
|
||||
actionID := packageAction{
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerBuildID: string(compilerBuildID),
|
||||
TinyGoVersion: goenv.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
EmbeddedFiles: make(map[string]string, len(allFiles)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
OptLevel: optLevel,
|
||||
SizeLevel: sizeLevel,
|
||||
UndefinedGlobals: undefinedGlobals,
|
||||
}
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
}
|
||||
for name, files := range allFiles {
|
||||
actionID.EmbeddedFiles[name] = files[0].Hash
|
||||
}
|
||||
for i, imported := range pkg.Pkg.Imports() {
|
||||
actionID.Imports[imported.Path()] = importedPackages[i].result
|
||||
}
|
||||
buf, err := json.Marshal(actionID)
|
||||
if err != nil {
|
||||
return err // shouldn't happen
|
||||
}
|
||||
hash := sha512.Sum512_224(buf)
|
||||
job.result = hex.EncodeToString(hash[:])
|
||||
return nil
|
||||
},
|
||||
}
|
||||
hash := sha512.Sum512_224(buf)
|
||||
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
|
||||
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
|
||||
|
||||
// Determine the path of the bitcode file (which is a serialized version
|
||||
// of a LLVM module).
|
||||
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
|
||||
packageBitcodePaths[pkg.ImportPath] = bitcodePath
|
||||
|
||||
// The package has not yet been compiled, so create a job to do so.
|
||||
// Now create the job to actually build the package. It will exit early
|
||||
// if the package is already compiled.
|
||||
job := &compileJob{
|
||||
description: "compile package " + pkg.ImportPath,
|
||||
run: func(*compileJob) error {
|
||||
description: "compile package " + pkg.ImportPath,
|
||||
dependencies: []*compileJob{packageActionIDJob},
|
||||
run: func(job *compileJob) error {
|
||||
job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
|
||||
// Acquire a lock (if supported).
|
||||
unlock := lock(bitcodePath + ".lock")
|
||||
unlock := lock(job.result + ".lock")
|
||||
defer unlock()
|
||||
|
||||
if _, err := os.Stat(bitcodePath); err == nil {
|
||||
if _, err := os.Stat(job.result); err == nil {
|
||||
// Already cached, don't recreate this package.
|
||||
return nil
|
||||
}
|
||||
@@ -287,6 +352,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Compile AST to IR. The compiler.CompilePackage function will
|
||||
// build the SSA as needed.
|
||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||
defer mod.Context().Dispose()
|
||||
defer mod.Dispose()
|
||||
if errs != nil {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
@@ -372,33 +439,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return errors.New("verification error after interpreting " + pkgInit.Name())
|
||||
}
|
||||
|
||||
// Run function passes for each function in the module.
|
||||
// These passes are intended to be run on each function right
|
||||
// after they're created to reduce IR size (and maybe also for
|
||||
// cache locality to improve performance), but for now they're
|
||||
// run here for each function in turn. Maybe this can be
|
||||
// improved in the future.
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
builder.SetSizeLevel(sizeLevel)
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
builder.PopulateFunc(funcPasses)
|
||||
funcPasses.InitializeFunc()
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
funcPasses.RunFunc(fn)
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
transform.OptimizePackage(mod, config)
|
||||
|
||||
// Serialize the LLVM module as a bitcode file.
|
||||
// Write to a temporary path that is renamed to the destination
|
||||
// file to avoid race conditions with other TinyGo invocatiosn
|
||||
// that might also be compiling this package at the same time.
|
||||
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
|
||||
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -418,13 +465,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||
// own, so create a somewhat useful error message here.
|
||||
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
|
||||
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result)
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(f.Name(), bitcodePath)
|
||||
return os.Rename(f.Name(), job.result)
|
||||
},
|
||||
}
|
||||
packageJobs = append(packageJobs, job)
|
||||
@@ -432,6 +479,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
|
||||
// Add job that links and optimizes all packages together.
|
||||
var mod llvm.Module
|
||||
defer func() {
|
||||
if !mod.IsNil() {
|
||||
ctx := mod.Context()
|
||||
mod.Dispose()
|
||||
ctx.Dispose()
|
||||
}
|
||||
}()
|
||||
var stackSizeLoads []string
|
||||
programJob := &compileJob{
|
||||
description: "link+optimize packages (LTO)",
|
||||
@@ -441,8 +495,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// anything, it only links the bitcode files together.
|
||||
ctx := llvm.NewContext()
|
||||
mod = ctx.NewModule("main")
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
|
||||
for _, pkgJob := range packageJobs {
|
||||
pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||
}
|
||||
@@ -534,6 +588,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer llvmBuf.Dispose()
|
||||
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||
case ".bc":
|
||||
var buf llvm.MemoryBuffer
|
||||
@@ -642,6 +697,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Add libc dependencies, if they exist.
|
||||
linkerDependencies = append(linkerDependencies, libcDependencies...)
|
||||
|
||||
// Add embedded files.
|
||||
linkerDependencies = append(linkerDependencies, embedFileObjects...)
|
||||
|
||||
// Strip debug information with -no-debug.
|
||||
if !config.Debug() {
|
||||
for _, tag := range config.BuildTags() {
|
||||
@@ -651,6 +709,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||
}
|
||||
}
|
||||
if config.GOOS() == "darwin" {
|
||||
// Debug information isn't stored in the binary itself on MacOS but
|
||||
// is left in the object files by default. The binary does store the
|
||||
// path to these object files though.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't store debug info in the executable by default")
|
||||
}
|
||||
if config.Target.Linker == "wasm-ld" {
|
||||
// Don't just strip debug information, also compress relocations
|
||||
// while we're at it. Relocations can only be compressed when debug
|
||||
@@ -660,21 +724,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// ld.lld is also used on Linux.
|
||||
ldflags = append(ldflags, "--strip-debug")
|
||||
} else {
|
||||
switch config.GOOS() {
|
||||
case "linux":
|
||||
// Either real linux or an embedded system (like AVR) that
|
||||
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||
// case (not ld.lld - that case is handled above).
|
||||
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||
case "darwin":
|
||||
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||
// information. Apple expects you to use the strip command
|
||||
// instead.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||
default:
|
||||
// Other OSes may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||
}
|
||||
// Other linkers may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,11 +745,24 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||
}
|
||||
if config.UseThinLTO() {
|
||||
ldflags = append(ldflags,
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||
"-plugin-opt=mcpu="+config.CPU(),
|
||||
"-plugin-opt=O"+strconv.Itoa(optLevel),
|
||||
"-plugin-opt=thinlto")
|
||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||
if config.GOOS() == "windows" {
|
||||
// Options for the MinGW wrapper for the lld COFF linker.
|
||||
ldflags = append(ldflags,
|
||||
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||
} else if config.GOOS() == "darwin" {
|
||||
// Options for the ld64-compatible lld linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(optLevel),
|
||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||
} else {
|
||||
// Options for the ELF linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(optLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||
)
|
||||
}
|
||||
if config.CodeModel() != "default" {
|
||||
ldflags = append(ldflags,
|
||||
"-mllvm", "-code-model="+config.CodeModel())
|
||||
@@ -881,6 +945,112 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
})
|
||||
}
|
||||
|
||||
// createEmbedObjectFile creates a new object file with the given contents, for
|
||||
// the embed package.
|
||||
func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, compilerConfig *compiler.Config) (string, error) {
|
||||
// TODO: this works for small files, but can be a problem for larger files.
|
||||
// For larger files, it seems more appropriate to generate the object file
|
||||
// manually without going through LLVM.
|
||||
// On the other hand, generating DWARF like we do here can be difficult
|
||||
// without assistance from LLVM.
|
||||
|
||||
// Create new LLVM module just for this file.
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
mod := ctx.NewModule("data")
|
||||
defer mod.Dispose()
|
||||
|
||||
// Create data global.
|
||||
value := ctx.ConstString(data, false)
|
||||
globalName := "embed/file_" + hexSum
|
||||
global := llvm.AddGlobal(mod, value.Type(), globalName)
|
||||
global.SetInitializer(value)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetAlignment(1)
|
||||
if compilerConfig.GOOS != "darwin" {
|
||||
// MachO doesn't support COMDATs, while COFF requires it (to avoid
|
||||
// "duplicate symbol" errors). ELF works either way.
|
||||
// Therefore, only use a COMDAT on non-MachO systems (aka non-MacOS).
|
||||
global.SetComdat(mod.Comdat(globalName))
|
||||
}
|
||||
|
||||
// Add DWARF debug information to this global, so that it is
|
||||
// correctly counted when compiling with the -size= flag.
|
||||
dibuilder := llvm.NewDIBuilder(mod)
|
||||
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||
File: sourceFile,
|
||||
Dir: sourceDir,
|
||||
Producer: "TinyGo",
|
||||
Optimized: false,
|
||||
})
|
||||
ditype := dibuilder.CreateArrayType(llvm.DIArrayType{
|
||||
SizeInBits: uint64(len(data)) * 8,
|
||||
AlignInBits: 8,
|
||||
ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||
Name: "byte",
|
||||
SizeInBits: 8,
|
||||
Encoding: llvm.DW_ATE_unsigned_char,
|
||||
}),
|
||||
Subscripts: []llvm.DISubrange{
|
||||
{
|
||||
Lo: 0,
|
||||
Count: int64(len(data)),
|
||||
},
|
||||
},
|
||||
})
|
||||
difile := dibuilder.CreateFile(sourceFile, sourceDir)
|
||||
diglobalexpr := dibuilder.CreateGlobalVariableExpression(difile, llvm.DIGlobalVariableExpression{
|
||||
Name: globalName,
|
||||
File: difile,
|
||||
Line: 1,
|
||||
Type: ditype,
|
||||
Expr: dibuilder.CreateExpression(nil),
|
||||
AlignInBits: 8,
|
||||
})
|
||||
global.AddMetadata(0, diglobalexpr)
|
||||
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
|
||||
ctx.MDString("Debug Info Version"),
|
||||
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
|
||||
}),
|
||||
)
|
||||
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
|
||||
ctx.MDString("Dwarf Version"),
|
||||
llvm.ConstInt(ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||
}),
|
||||
)
|
||||
dibuilder.Finalize()
|
||||
dibuilder.Destroy()
|
||||
|
||||
// Write this LLVM module out as an object file.
|
||||
machine, err := compiler.NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer machine.Dispose()
|
||||
outfile, err := os.CreateTemp(tmpdir, "embed-"+hexSum+"-*.o")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer outfile.Close()
|
||||
buf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer buf.Dispose()
|
||||
_, err = outfile.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return outfile.Name(), outfile.Close()
|
||||
}
|
||||
|
||||
// optimizeProgram runs a series of optimizations and transformations that are
|
||||
// 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.
|
||||
@@ -935,17 +1105,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
}
|
||||
|
||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
||||
// disabled) unless it is explicitly disabled with a function attribute.
|
||||
// This is a problem, as it tries to emit them and prints an error when it
|
||||
// can't with this feature disabled.
|
||||
// Because as of september 2020 tail calls are not yet widely supported,
|
||||
// they need to be disabled until they are widely supported (at which point
|
||||
// the +tail-call target feautre can be set).
|
||||
if strings.HasPrefix(config.Triple(), "wasm") {
|
||||
transform.DisableTailCalls(mod)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m0",
|
||||
"cortex-m0plus",
|
||||
"cortex-m3",
|
||||
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
||||
"cortex-m33",
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
|
||||
+8
-2
@@ -40,7 +40,6 @@ var genericBuiltins = []string{
|
||||
"divdf3.c",
|
||||
"divdi3.c",
|
||||
"divmoddi4.c",
|
||||
"divmodsi4.c",
|
||||
"divsc3.c",
|
||||
"divsf3.c",
|
||||
"divsi3.c",
|
||||
@@ -127,7 +126,6 @@ var genericBuiltins = []string{
|
||||
"ucmpti2.c",
|
||||
"udivdi3.c",
|
||||
"udivmoddi4.c",
|
||||
"udivmodsi4.c",
|
||||
"udivmodti4.c",
|
||||
"udivsi3.c",
|
||||
"udivti3.c",
|
||||
@@ -154,6 +152,14 @@ var aeabiBuiltins = []string{
|
||||
"arm/aeabi_memset.S",
|
||||
"arm/aeabi_uidivmod.S",
|
||||
"arm/aeabi_uldivmod.S",
|
||||
|
||||
// These two are not technically EABI builtins but are used by them and only
|
||||
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||
// other architectures.
|
||||
// Most importantly, they have a different calling convention on AVR so
|
||||
// should not be used on AVR.
|
||||
"divmodsi4.c",
|
||||
"udivmodsi4.c",
|
||||
}
|
||||
|
||||
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ func LookupCommand(name string) (string, error) {
|
||||
}
|
||||
return cmdName, nil
|
||||
}
|
||||
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
}
|
||||
|
||||
func execCommand(name string, args ...string) error {
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
if major != 1 || minor < 15 || minor > 18 {
|
||||
return nil, fmt.Errorf("requires go version 1.15 through 1.18, got go%d.%d", major, minor)
|
||||
if major != 1 || minor < 16 || minor > 19 {
|
||||
return nil, fmt.Errorf("requires go version 1.16 through 1.19, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Create a job that builds a Darwin libSystem.dylib stub library. This library
|
||||
@@ -39,12 +38,8 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
||||
|
||||
// Link object file to dynamic library.
|
||||
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
||||
flavor := "darwin"
|
||||
if strings.Split(llvm.Version, ".")[0] < "13" {
|
||||
flavor = "darwinnew" // needed on LLVM 12 and below
|
||||
}
|
||||
flags = []string{
|
||||
"-flavor", flavor,
|
||||
"-flavor", "darwin",
|
||||
"-demangle",
|
||||
"-dynamic",
|
||||
"-dylib",
|
||||
|
||||
@@ -148,6 +148,8 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// However, ARM has not done this.
|
||||
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
||||
args = append(args, "-march="+cpu)
|
||||
} else if strings.HasPrefix(target, "avr") {
|
||||
args = append(args, "-mmcu="+cpu)
|
||||
} else {
|
||||
args = append(args, "-mcpu="+cpu)
|
||||
}
|
||||
@@ -155,6 +157,13 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
}
|
||||
if strings.HasPrefix(target, "avr") {
|
||||
// AVR defaults to C float and double both being 32-bit. This deviates
|
||||
// from what most code (and certainly compiler-rt) expects. So we need
|
||||
// to force the compiler to use 64-bit floating point numbers for
|
||||
// double.
|
||||
args = append(args, "-mdouble=64")
|
||||
}
|
||||
if strings.HasPrefix(target, "riscv32-") {
|
||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||
}
|
||||
|
||||
@@ -115,6 +115,7 @@ var Musl = Library{
|
||||
"internal/libc.c",
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"malloc/*.c",
|
||||
"mman/*.c",
|
||||
"signal/*.c",
|
||||
|
||||
+193
-7
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
@@ -116,7 +117,7 @@ var (
|
||||
// alloc: heap allocations during init interpretation
|
||||
// pack: data created when storing a constant in an interface for example
|
||||
// string: buffer behind strings
|
||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
|
||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
|
||||
|
||||
// Reflect sidetables. Created by the reflect lowering pass.
|
||||
// See src/reflect/sidetables.go.
|
||||
@@ -126,7 +127,7 @@ var (
|
||||
// readProgramSizeFromDWARF reads the source location for each line of code and
|
||||
// each variable in the program, as far as this is stored in the DWARF debug
|
||||
// information.
|
||||
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLine, error) {
|
||||
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone bool) ([]addressLine, error) {
|
||||
r := data.Reader()
|
||||
var lines []*dwarf.LineFile
|
||||
var addresses []addressLine
|
||||
@@ -168,7 +169,7 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if prevLineEntry.EndSequence && lineEntry.Address == 0 {
|
||||
if prevLineEntry.EndSequence && lineEntry.Address == 0 && skipTombstone {
|
||||
// Tombstone value. This symbol has been removed, for
|
||||
// example by the --gc-sections linker flag. It is still
|
||||
// here in the debug information because the linker can't
|
||||
@@ -177,6 +178,10 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
// skipped.
|
||||
// For more details, see (among others):
|
||||
// https://reviews.llvm.org/D84825
|
||||
// The value 0 can however really occur in object files,
|
||||
// that typically start at address 0. So don't skip
|
||||
// tombstone values in object files (like when parsing MachO
|
||||
// files).
|
||||
for {
|
||||
err := lr.Next(&lineEntry)
|
||||
if err != nil {
|
||||
@@ -255,6 +260,65 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// Read a MachO object file and return a line table.
|
||||
// Also return an index from symbol name to start address in the line table.
|
||||
func readMachOSymbolAddresses(path string) (map[string]int, []addressLine, error) {
|
||||
// Some constants from mach-o/nlist.h
|
||||
// See: https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
|
||||
const (
|
||||
N_STAB = 0xe0
|
||||
N_TYPE = 0x0e // bitmask for N_TYPE field
|
||||
N_SECT = 0xe // one of the possible type in the N_TYPE field
|
||||
)
|
||||
|
||||
// Read DWARF from the given object file.
|
||||
file, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
dwarf, err := file.DWARF()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
lines, err := readProgramSizeFromDWARF(dwarf, 0, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Make a map from start addresses to indices in the line table (because the
|
||||
// line table is a slice, not a map).
|
||||
addressToLine := make(map[uint64]int, len(lines))
|
||||
for i, line := range lines {
|
||||
if _, ok := addressToLine[line.Address]; ok {
|
||||
addressToLine[line.Address] = -1
|
||||
continue
|
||||
}
|
||||
addressToLine[line.Address] = i
|
||||
}
|
||||
|
||||
// Make a map that for each symbol gives the start index in the line table.
|
||||
addresses := make(map[string]int, len(addressToLine))
|
||||
for _, symbol := range file.Symtab.Syms {
|
||||
if symbol.Type&N_STAB != 0 {
|
||||
continue // STABS entry, ignore
|
||||
}
|
||||
if symbol.Type&0x0e != N_SECT {
|
||||
continue // undefined symbol
|
||||
}
|
||||
if index, ok := addressToLine[symbol.Value]; ok && index >= 0 {
|
||||
if _, ok := addresses[symbol.Name]; ok {
|
||||
// There is a duplicate. Mark it as unavailable.
|
||||
addresses[symbol.Name] = -1
|
||||
continue
|
||||
}
|
||||
addresses[symbol.Name] = index
|
||||
}
|
||||
}
|
||||
|
||||
return addresses, lines, nil
|
||||
}
|
||||
|
||||
// loadProgramSize calculate a program/data size breakdown of each package for a
|
||||
// given ELF file.
|
||||
// If the file doesn't contain DWARF debug information, the returned program
|
||||
@@ -277,7 +341,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0)
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
@@ -368,11 +432,133 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if file, err := macho.NewFile(f); err == nil {
|
||||
// Read segments, for use while reading through sections.
|
||||
segments := map[string]*macho.Segment{}
|
||||
for _, load := range file.Loads {
|
||||
switch load := load.(type) {
|
||||
case *macho.Segment:
|
||||
segments[load.Name] = load
|
||||
}
|
||||
}
|
||||
|
||||
// Read MachO sections.
|
||||
for _, section := range file.Sections {
|
||||
sectionType := section.Flags & 0xff
|
||||
sectionFlags := section.Flags >> 8
|
||||
segment := segments[section.Seg]
|
||||
// For the constants used here, see:
|
||||
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
|
||||
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
|
||||
// Section containing only instructions.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryCode,
|
||||
})
|
||||
} else if sectionType == 1 { // S_ZEROFILL
|
||||
// Section filled with zeroes on demand.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryBSS,
|
||||
})
|
||||
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
|
||||
// Protection doesn't allow writes, so mark this section read-only.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryROData,
|
||||
})
|
||||
} else {
|
||||
// The rest is assumed to be regular data.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Read DWARF information.
|
||||
// The data isn't stored directly in the binary as in most executable
|
||||
// formats. Instead, it is left in the object files that were used as a
|
||||
// basis for linking. The executable does however contain STABS debug
|
||||
// information that points to the source object file and is used by
|
||||
// debuggers.
|
||||
// For more information:
|
||||
// http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme
|
||||
var objSymbolNames map[string]int
|
||||
var objAddresses []addressLine
|
||||
var previousSymbol macho.Symbol
|
||||
for _, symbol := range file.Symtab.Syms {
|
||||
// STABS constants, from mach-o/stab.h:
|
||||
// https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/stab.h.auto.html
|
||||
const (
|
||||
N_GSYM = 0x20
|
||||
N_FUN = 0x24
|
||||
N_STSYM = 0x26
|
||||
N_SO = 0x64
|
||||
N_OSO = 0x66
|
||||
)
|
||||
if symbol.Type == N_OSO {
|
||||
// Found an object file. Now try to parse it.
|
||||
objSymbolNames, objAddresses, err = readMachOSymbolAddresses(symbol.Name)
|
||||
if err != nil && sizesDebug {
|
||||
// Errors are normally ignored. If there is an error, it's
|
||||
// simply treated as that the DWARF is not available.
|
||||
fmt.Fprintf(os.Stderr, "could not read DWARF from file %s: %s\n", symbol.Name, err)
|
||||
}
|
||||
} else if symbol.Type == N_FUN {
|
||||
// Found a function.
|
||||
// The way this is encoded is a bit weird. MachO symbols don't
|
||||
// have a length. What I've found is that the length is encoded
|
||||
// by first having a N_FUN symbol as usual, and then having a
|
||||
// symbol with a zero-length name that has the value not set to
|
||||
// the address of the symbol but to the length. So in order to
|
||||
// get both the address and the length, we look for a symbol
|
||||
// with a name followed by a symbol without a name.
|
||||
if symbol.Name == "" && previousSymbol.Type == N_FUN && previousSymbol.Name != "" {
|
||||
// Functions are encoded as many small chunks in the line
|
||||
// table (one or a few instructions per source line). But
|
||||
// the symbol length covers the whole symbols, over many
|
||||
// lines and possibly including inlined functions. So we
|
||||
// continue to iterate through the objAddresses slice until
|
||||
// we've found all the source lines that are part of this
|
||||
// symbol.
|
||||
address := previousSymbol.Value
|
||||
length := symbol.Value
|
||||
if index, ok := objSymbolNames[previousSymbol.Name]; ok && index >= 0 {
|
||||
for length > 0 {
|
||||
line := objAddresses[index]
|
||||
line.Address = address
|
||||
if line.Length > length {
|
||||
// Line extends beyond the end of te symbol?
|
||||
// Weird, shouldn't happen.
|
||||
break
|
||||
}
|
||||
addresses = append(addresses, line)
|
||||
index++
|
||||
length -= line.Length
|
||||
address += line.Length
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if symbol.Type == N_GSYM || symbol.Type == N_STSYM {
|
||||
// Global variables.
|
||||
if index, ok := objSymbolNames[symbol.Name]; ok {
|
||||
address := objAddresses[index]
|
||||
address.Address = symbol.Value
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
}
|
||||
previousSymbol = symbol
|
||||
}
|
||||
} else if file, err := pe.NewFile(f); err == nil {
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0)
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
@@ -442,9 +628,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
const codeOffset = 0x8000_0000_0000_0000
|
||||
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, err := file.DWARF()
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, codeOffset)
|
||||
addresses, err = readProgramSizeFromDWARF(data, codeOffset, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
|
||||
@@ -54,7 +54,7 @@ func RunTool(tool string, args ...string) error {
|
||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||
case "ld.lld":
|
||||
switch linker {
|
||||
case "darwin", "darwinnew": // darwinnew is only needed for LLVM 12 and below
|
||||
case "darwin":
|
||||
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
||||
case "elf":
|
||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||
|
||||
+365
-531
File diff suppressed because it is too large
Load Diff
+383
-199
@@ -72,7 +72,11 @@ var diagnosticSeverity = [...]string{
|
||||
C.CXDiagnostic_Fatal: "fatal",
|
||||
}
|
||||
|
||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
|
||||
// Alias so that cgo.go (which doesn't import Clang related stuff and is in
|
||||
// theory decoupled from Clang) can also use this type.
|
||||
type clangCursor = C.GoCXCursor
|
||||
|
||||
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
|
||||
index := C.clang_createIndex(0, 0)
|
||||
defer C.clang_disposeIndex(index)
|
||||
|
||||
@@ -119,8 +123,8 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||
location := C.clang_getDiagnosticLocation(diagnostic)
|
||||
pos := p.getClangLocationPosition(location, unit)
|
||||
p.addError(pos, severity+": "+spelling)
|
||||
pos := f.getClangLocationPosition(location, unit)
|
||||
f.addError(pos, severity+": "+spelling)
|
||||
}
|
||||
for i := 0; i < numDiagnostics; i++ {
|
||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||
@@ -135,7 +139,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||
}
|
||||
|
||||
// Extract information required by CGo.
|
||||
ref := storedRefs.Put(p)
|
||||
ref := storedRefs.Put(f)
|
||||
defer storedRefs.Remove(ref)
|
||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||
@@ -155,35 +159,64 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||
|
||||
// Hash the contents if it isn't hashed yet.
|
||||
if _, ok := p.visitedFiles[path]; !ok {
|
||||
if _, ok := f.visitedFiles[path]; !ok {
|
||||
// already stored
|
||||
sum := sha512.Sum512_224(data)
|
||||
p.visitedFiles[path] = sum[:]
|
||||
f.visitedFiles[path] = sum[:]
|
||||
}
|
||||
}
|
||||
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||
defer storedRefs.Remove(inclusionCallbackRef)
|
||||
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||
|
||||
// Do all the C AST operations inside a callback. This makes sure that
|
||||
// libclang related memory is only freed after it is not necessary anymore.
|
||||
callback(f.names)
|
||||
}
|
||||
|
||||
//export tinygo_clang_globals_visitor
|
||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
||||
// it.
|
||||
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
|
||||
kind := C.tinygo_clang_getCursorKind(c)
|
||||
pos := p.getCursorPosition(c)
|
||||
pos := f.getCursorPosition(c)
|
||||
switch kind {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
fn := &functionInfo{
|
||||
pos: pos,
|
||||
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "C." + name,
|
||||
}
|
||||
args := make([]*ast.Field, numArgs)
|
||||
decl := &ast.FuncDecl{
|
||||
Doc: &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: pos - 1,
|
||||
Text: "//export " + name,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: pos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: pos,
|
||||
List: args,
|
||||
Closing: pos,
|
||||
},
|
||||
},
|
||||
}
|
||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||
Slash: pos - 1,
|
||||
Text: "//go:variadic",
|
||||
})
|
||||
}
|
||||
p.functions[name] = fn
|
||||
for i := 0; i < numArgs; i++ {
|
||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||
@@ -191,50 +224,108 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
if argName == "" {
|
||||
argName = "$" + strconv.Itoa(i)
|
||||
}
|
||||
fn.args = append(fn.args, paramInfo{
|
||||
name: argName,
|
||||
typeExpr: p.makeDecayingASTType(argType, pos),
|
||||
})
|
||||
args[i] = &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: argName,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: argName,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: f.makeDecayingASTType(argType, pos),
|
||||
}
|
||||
}
|
||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||
if resultType.kind != C.CXType_Void {
|
||||
fn.results = &ast.FieldList{
|
||||
decl.Type.Results = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
{
|
||||
Type: p.makeASTType(resultType, pos),
|
||||
Type: f.makeASTType(resultType, pos),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case C.CXCursor_StructDecl:
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
obj.Decl = decl
|
||||
return decl, nil
|
||||
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||
typ := f.makeASTRecordType(c, pos)
|
||||
typeName := "C." + name
|
||||
typeExpr := typ.typeExpr
|
||||
if typ.unionSize != 0 {
|
||||
// Convert to a single-field struct type.
|
||||
typeExpr = f.makeUnionField(typ)
|
||||
}
|
||||
p.makeASTType(typ, pos)
|
||||
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:
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
typeName := "C." + name
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
p.makeASTType(typedefType, pos)
|
||||
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:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
p.globals[name] = globalInfo{
|
||||
typeExpr: p.makeASTType(cursorType, pos),
|
||||
pos: pos,
|
||||
typeExpr := f.makeASTType(cursorType, pos)
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: pos,
|
||||
Tok: token.VAR,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
Doc: &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: pos - 1,
|
||||
Text: "//go:extern " + name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_MacroDefinition:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||
start := C.clang_getRangeStart(sourceRange)
|
||||
end := C.clang_getRangeEnd(sourceRange)
|
||||
@@ -242,17 +333,17 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
var startOffset, endOffset C.unsigned
|
||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||
if file == nil {
|
||||
p.addError(pos, "internal error: could not find file where macro is defined")
|
||||
break
|
||||
f.addError(pos, "internal error: could not find file where macro is defined")
|
||||
return nil, nil
|
||||
}
|
||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||
if file != endFile {
|
||||
p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||
break
|
||||
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||
return nil, nil
|
||||
}
|
||||
if startOffset > endOffset {
|
||||
p.addError(pos, "internal error: start offset of macro is after end offset")
|
||||
break
|
||||
f.addError(pos, "internal error: start offset of macro is after end offset")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// read file contents and extract the relevant byte range
|
||||
@@ -260,31 +351,94 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
if endOffset >= C.uint(size) {
|
||||
p.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||
break
|
||||
f.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||
return nil, nil
|
||||
}
|
||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||
if !strings.HasPrefix(source, name) {
|
||||
p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||
break
|
||||
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||
return nil, nil
|
||||
}
|
||||
value := source[len(name):]
|
||||
// Try to convert this #define into a Go constant expression.
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
|
||||
if scannerError != nil {
|
||||
p.errors = append(p.errors, *scannerError)
|
||||
f.errors = append(f.errors, *scannerError)
|
||||
return nil, nil
|
||||
}
|
||||
if expr != nil {
|
||||
// Parsing was successful.
|
||||
p.constants[name] = constantInfo{expr, pos}
|
||||
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.CONST,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_EnumDecl:
|
||||
// Visit all enums, because the fields may be used even when the enum
|
||||
// type itself is not.
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
p.makeASTType(typ, pos)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + 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.
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + 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)
|
||||
expr := &ast.BasicLit{
|
||||
ValuePos: pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(value), 10),
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.CONST,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
default:
|
||||
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
|
||||
return nil, nil
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
func getString(clangString C.CXString) (s string) {
|
||||
@@ -294,6 +448,49 @@ func getString(clangString C.CXString) (s string) {
|
||||
return
|
||||
}
|
||||
|
||||
//export tinygo_clang_globals_visitor
|
||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
|
||||
switch C.tinygo_clang_getCursorKind(c) {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_StructDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
f.names["struct_"+name] = c
|
||||
}
|
||||
case C.CXCursor_UnionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
f.names["union_"+name] = c
|
||||
}
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_MacroDefinition:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_EnumDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
// Named enum, which can be referenced from Go using C.enum_foo.
|
||||
f.names["enum_"+name] = c
|
||||
}
|
||||
// The enum fields are in global scope, so recurse to visit them.
|
||||
return C.CXChildVisit_Recurse
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
// We arrive here because of the "Recurse" above.
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
||||
@@ -391,7 +588,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
||||
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
||||
// types (arrays in function parameters, etc). It is otherwise identical to
|
||||
// makeASTType.
|
||||
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
// Strip typedefs, if any.
|
||||
underlyingType := typ
|
||||
if underlyingType.kind == C.CXType_Typedef {
|
||||
@@ -417,15 +614,15 @@ func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
pointeeType := C.clang_getElementType(underlyingType)
|
||||
return &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: p.makeASTType(pointeeType, pos),
|
||||
X: f.makeASTType(pointeeType, pos),
|
||||
}
|
||||
}
|
||||
return p.makeASTType(typ, pos)
|
||||
return f.makeASTType(typ, pos)
|
||||
}
|
||||
|
||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||
// it converts a libclang type to a type in the Go AST.
|
||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
var typeName string
|
||||
switch typ.kind {
|
||||
case C.CXType_Char_S, C.CXType_Char_U:
|
||||
@@ -486,7 +683,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
return &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: p.makeASTType(pointeeType, pos),
|
||||
X: f.makeASTType(pointeeType, pos),
|
||||
}
|
||||
case C.CXType_ConstantArray:
|
||||
return &ast.ArrayType{
|
||||
@@ -496,7 +693,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||
},
|
||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
||||
Elt: f.makeASTType(C.clang_getElementType(typ), pos),
|
||||
}
|
||||
case C.CXType_FunctionProto:
|
||||
// Be compatible with gc, which uses the *[0]byte type for function
|
||||
@@ -517,71 +714,21 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
case C.CXType_Typedef:
|
||||
name := getString(C.clang_getTypedefName(typ))
|
||||
if _, ok := p.typedefs[name]; !ok {
|
||||
p.typedefs[name] = nil // don't recurse
|
||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
expr := p.makeASTType(underlyingType, pos)
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
expr := expr.(*ast.Ident)
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch expr.Name {
|
||||
case "C.char":
|
||||
if typeSize != 1 {
|
||||
// This happens for some very special purpose architectures
|
||||
// (DSPs etc.) that are not currently targeted.
|
||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||
}
|
||||
switch underlyingType.kind {
|
||||
case C.CXType_Char_S:
|
||||
expr.Name = "int8"
|
||||
case C.CXType_Char_U:
|
||||
expr.Name = "uint8"
|
||||
}
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "int8"
|
||||
case 2:
|
||||
expr.Name = "int16"
|
||||
case 4:
|
||||
expr.Name = "int32"
|
||||
case 8:
|
||||
expr.Name = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "uint8"
|
||||
case 2:
|
||||
expr.Name = "uint16"
|
||||
case 4:
|
||||
expr.Name = "uint32"
|
||||
case 8:
|
||||
expr.Name = "uint64"
|
||||
}
|
||||
}
|
||||
}
|
||||
p.typedefs[name] = &typedefInfo{
|
||||
typeExpr: expr,
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Name: f.getASTDeclName(name, c, false),
|
||||
}
|
||||
case C.CXType_Elaborated:
|
||||
underlying := C.clang_Type_getNamedType(typ)
|
||||
switch underlying.kind {
|
||||
case C.CXType_Record:
|
||||
return p.makeASTType(underlying, pos)
|
||||
return f.makeASTType(underlying, pos)
|
||||
case C.CXType_Enum:
|
||||
return p.makeASTType(underlying, pos)
|
||||
return f.makeASTType(underlying, pos)
|
||||
default:
|
||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
||||
p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
typeName = "<unknown>"
|
||||
}
|
||||
case C.CXType_Record:
|
||||
@@ -599,63 +746,46 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
if name == "" {
|
||||
// Anonymous record, probably inside a typedef.
|
||||
typeInfo := p.makeASTRecordType(cursor, pos)
|
||||
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
|
||||
// This record is a union or is a struct with bitfields, so we
|
||||
// have to declare it as a named type (for getters/setters to
|
||||
// work).
|
||||
p.anonStructNum++
|
||||
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
|
||||
p.elaboratedTypes[cgoName] = typeInfo
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||
var file C.CXFile
|
||||
var line C.unsigned
|
||||
var column C.unsigned
|
||||
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
||||
location := token.Position{
|
||||
Filename: getString(C.clang_getFileName(file)),
|
||||
Line: int(line),
|
||||
Column: int(column),
|
||||
}
|
||||
return typeInfo.typeExpr
|
||||
if location.Filename == "" || location.Line == 0 {
|
||||
// Not sure when this would happen, but protect from it anyway.
|
||||
f.addError(pos, "could not find file/line information")
|
||||
}
|
||||
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||
} else {
|
||||
cgoName := cgoRecordPrefix + name
|
||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
name = cgoRecordPrefix + name
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: f.getASTDeclName(name, cursor, false),
|
||||
}
|
||||
case C.CXType_Enum:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
||||
if name == "" {
|
||||
// anonymous enum
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
return p.makeASTType(underlying, pos)
|
||||
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
|
||||
} else {
|
||||
// named enum
|
||||
if _, ok := p.enums[name]; !ok {
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
p.enums[name] = enumInfo{
|
||||
typeExpr: p.makeASTType(underlying, pos),
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C.enum_" + name,
|
||||
}
|
||||
name = "enum_" + name
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: f.getASTDeclName(name, cursor, false),
|
||||
}
|
||||
}
|
||||
if typeName == "" {
|
||||
// Report this as an error.
|
||||
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
||||
p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||
typeName = "C.<unknown>"
|
||||
}
|
||||
return &ast.Ident{
|
||||
@@ -664,9 +794,80 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
}
|
||||
|
||||
// getIntegerType returns an AST node that defines types such as C.int.
|
||||
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
|
||||
pos := p.getCursorPosition(cursor)
|
||||
|
||||
// Find a Go type that matches the size and signedness of the given C type.
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
|
||||
var goName string
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch name {
|
||||
case "C.char":
|
||||
if typeSize != 1 {
|
||||
// This happens for some very special purpose architectures
|
||||
// (DSPs etc.) that are not currently targeted.
|
||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||
}
|
||||
switch underlyingType.kind {
|
||||
case C.CXType_Char_S:
|
||||
goName = "int8"
|
||||
case C.CXType_Char_U:
|
||||
goName = "uint8"
|
||||
}
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "int8"
|
||||
case 2:
|
||||
goName = "int16"
|
||||
case 4:
|
||||
goName = "int32"
|
||||
case 8:
|
||||
goName = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "uint8"
|
||||
case 2:
|
||||
goName = "uint16"
|
||||
case 4:
|
||||
goName = "uint32"
|
||||
case 8:
|
||||
goName = "uint64"
|
||||
}
|
||||
}
|
||||
|
||||
if goName == "" { // should not happen
|
||||
p.addError(pos, "internal error: did not find Go type for C type "+name)
|
||||
goName = "int"
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||
// a Go struct type.
|
||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
@@ -676,11 +877,11 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
bitfieldNum := 0
|
||||
ref := storedRefs.Put(struct {
|
||||
fieldList *ast.FieldList
|
||||
pkg *cgoPackage
|
||||
file *cgoFile
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||
renameFieldKeywords(fieldList)
|
||||
@@ -709,13 +910,13 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
}
|
||||
if bitfieldList != nil {
|
||||
// This is valid C... but please don't do this.
|
||||
p.addError(pos, "bitfield in a union is not supported")
|
||||
f.addError(pos, "bitfield in a union is not supported")
|
||||
}
|
||||
typ := C.tinygo_clang_getCursorType(cursor)
|
||||
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
||||
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
||||
if sizeInBytes == 0 {
|
||||
p.addError(pos, "zero-length union is not supported")
|
||||
f.addError(pos, "zero-length union is not supported")
|
||||
}
|
||||
typeInfo.unionSize = sizeInBytes
|
||||
typeInfo.unionAlign = alignInBytes
|
||||
@@ -723,7 +924,7 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
default:
|
||||
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||
return &elaboratedTypeInfo{
|
||||
typeExpr: &ast.StructType{
|
||||
Struct: pos,
|
||||
@@ -737,17 +938,17 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||
fieldList *ast.FieldList
|
||||
pkg *cgoPackage
|
||||
file *cgoFile
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
})
|
||||
fieldList := passed.fieldList
|
||||
p := passed.pkg
|
||||
f := passed.file
|
||||
inBitfield := passed.inBitfield
|
||||
bitfieldNum := passed.bitfieldNum
|
||||
bitfieldList := passed.bitfieldList
|
||||
pos := p.getCursorPosition(c)
|
||||
pos := f.getCursorPosition(c)
|
||||
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
||||
case C.CXCursor_FieldDecl:
|
||||
// Expected. This is a regular field.
|
||||
@@ -756,7 +957,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
return C.CXChildVisit_Continue
|
||||
default:
|
||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||
p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
@@ -767,14 +968,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
}
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
field := &ast.Field{
|
||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
||||
Type: f.makeASTType(typ, f.getCursorPosition(c)),
|
||||
}
|
||||
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
||||
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
||||
bitfieldOffset := offsetof % alignOf
|
||||
if bitfieldOffset != 0 {
|
||||
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
||||
p.addError(pos, "expected a bitfield")
|
||||
f.addError(pos, "expected a bitfield")
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
if !*inBitfield {
|
||||
@@ -821,23 +1022,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_enum_visitor
|
||||
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
pos := p.getCursorPosition(c)
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
p.constants[name] = constantInfo{
|
||||
expr: &ast.BasicLit{
|
||||
ValuePos: pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(value), 10),
|
||||
},
|
||||
pos: pos,
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_inclusion_visitor
|
||||
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
//go:build !byollvm && llvm11
|
||||
// +build !byollvm,llvm11
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@11/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@11/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,16 +0,0 @@
|
||||
//go:build !byollvm && llvm12
|
||||
// +build !byollvm,llvm12
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@12/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@12/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@12/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:build !byollvm && !llvm11 && !llvm12 && !llvm14
|
||||
// +build !byollvm,!llvm11,!llvm12,!llvm14
|
||||
//go:build !byollvm && !llvm14
|
||||
// +build !byollvm,!llvm14
|
||||
|
||||
package cgo
|
||||
|
||||
|
||||
Vendored
+13
-20
@@ -24,23 +24,16 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
Vendored
+15
-22
@@ -24,26 +24,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
const C.bar = C.foo
|
||||
const C.foo = 3
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
const C.foo = 3
|
||||
const C.bar = C.foo
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ import "C"
|
||||
//line errors.go:100
|
||||
var (
|
||||
// constant too large
|
||||
_ C.uint8_t = 2 << 10
|
||||
_ C.char = 2 << 10
|
||||
|
||||
// z member does not exist
|
||||
_ C.point_t = C.point_t{z: 3}
|
||||
|
||||
Vendored
+18
-24
@@ -6,7 +6,7 @@
|
||||
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
||||
|
||||
// Type checking errors after CGo processing:
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||
// testdata/errors.go:105: unknown field z in struct literal
|
||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||
@@ -38,29 +38,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.point_t = struct {
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C._Ctype_struct___0 struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point_t = C._Ctype_struct___0
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
|
||||
Vendored
+14
-21
@@ -29,26 +29,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
const C.BAR = 3
|
||||
const C.FOO_H = 1
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+18
-23
@@ -24,41 +24,36 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
//export foo
|
||||
func C.foo(a C.int, b C.int) C.int
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic0
|
||||
//go:variadic
|
||||
func C.variadic0()
|
||||
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic2
|
||||
//go:variadic
|
||||
func C.variadic2(x C.int, y C.int)
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
var C.variadic2$funcaddr unsafe.Pointer
|
||||
|
||||
//go:extern someValue
|
||||
var C.someValue C.int
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+98
-104
@@ -24,132 +24,126 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
const C.option2A = 20
|
||||
const C.optionA = 0
|
||||
const C.optionB = 1
|
||||
const C.optionC = -5
|
||||
const C.optionD = -4
|
||||
const C.optionE = 10
|
||||
const C.optionF = 11
|
||||
const C.optionG = 12
|
||||
const C.unused1 = 5
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.bitfield_t = C.struct_4
|
||||
type C.myIntArray = [10]C.int
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C.myint = C.int
|
||||
type C.option2_t = C.uint
|
||||
type C.option_t = C.enum_option
|
||||
type C.point2d_t = struct {
|
||||
type C._Ctype_struct___0 struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_nested_t = struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
|
||||
coord C.union_2
|
||||
}
|
||||
type C.types_t = struct {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.int
|
||||
}
|
||||
type C.union1_t = struct{ i C.int }
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C.union3_t = C.union_1
|
||||
type C.union_nested_t = C.union_3
|
||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
||||
|
||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C.struct_4) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C.struct_4) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.struct_4 struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
|
||||
d C.uchar
|
||||
e C.uchar
|
||||
}
|
||||
type C.point2d_t = C._Ctype_struct___0
|
||||
type C.struct_point3d struct {
|
||||
x C.int
|
||||
y C.int
|
||||
z C.int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_type1 struct {
|
||||
_type C.int
|
||||
__type C.int
|
||||
___type C.int
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
type C._Ctype_union___1 struct{ i C.int }
|
||||
type C.union1_t = C._Ctype_union___1
|
||||
type C._Ctype_union___2 struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||
|
||||
type C.union_1 struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_2) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *C._Ctype_union___2) unionfield_i() *C.int {
|
||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_2) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *C._Ctype_union___2) unionfield_d() *float64 {
|
||||
return (*float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___2) unionfield_s() *C.short {
|
||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_2 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C.union_3) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_3 struct{ $union [2]uint64 }
|
||||
type C.union3_t = C._Ctype_union___2
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
type C.enum_option C.int
|
||||
type C.enum_unused C.uint
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C._Ctype_union___3 struct{ arr [10]C.uchar }
|
||||
type C.unionarray_t = C._Ctype_union___3
|
||||
type C._Ctype_union___5 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C._Ctype_struct___4 struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
|
||||
coord C._Ctype_union___5
|
||||
}
|
||||
type C.struct_nested_t = C._Ctype_struct___4
|
||||
type C._Ctype_union___6 struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_nested_t = C._Ctype_union___6
|
||||
type C.enum_option = C.int
|
||||
type C.option_t = C.enum_option
|
||||
type C._Ctype_enum___7 = C.uint
|
||||
type C.option2_t = C._Ctype_enum___7
|
||||
type C._Ctype_struct___8 struct {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.int
|
||||
}
|
||||
type C.types_t = C._Ctype_struct___8
|
||||
type C.myIntArray = [10]C.int
|
||||
type C._Ctype_struct___9 struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
|
||||
d C.uchar
|
||||
e C.uchar
|
||||
}
|
||||
|
||||
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.bitfield_t = C._Ctype_struct___9
|
||||
|
||||
@@ -192,15 +192,6 @@ func (c *Config) UseThinLTO() bool {
|
||||
// through a plugin, but it's too much hassle to set up.
|
||||
return false
|
||||
}
|
||||
if len(parts) >= 2 && strings.HasPrefix(parts[2], "macos") {
|
||||
// We use an external linker here at the moment.
|
||||
return false
|
||||
}
|
||||
if len(parts) >= 2 && parts[2] == "windows" {
|
||||
// Linker error (undefined runtime.trackedGlobalsBitmap) when linking
|
||||
// for Windows. Disable it for now until that's figured out and fixed.
|
||||
return false
|
||||
}
|
||||
// Other architectures support ThinLTO.
|
||||
return true
|
||||
}
|
||||
|
||||
+3
-14
@@ -12,11 +12,9 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Target specification for a given target. Used for bare metal targets.
|
||||
@@ -266,12 +264,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
spec.Libc = "darwin-libSystem"
|
||||
arch := strings.Split(triple, "-")[0]
|
||||
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
|
||||
flavor := "darwin"
|
||||
if strings.Split(llvm.Version, ".")[0] < "13" {
|
||||
flavor = "darwinnew" // needed on LLVM 12 and below
|
||||
}
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-flavor", flavor,
|
||||
"-flavor", "darwin",
|
||||
"-dead_strip",
|
||||
"-arch", arch,
|
||||
"-platform_version", "macos", platformVersion, platformVersion,
|
||||
@@ -296,13 +290,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
"--image-base", "0x400000",
|
||||
"--gc-sections",
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||
if llvmMajor >= 12 {
|
||||
// This flag was added in LLVM 12. At the same time, LLVM 12
|
||||
// switched the default from --dynamicbase to --no-dynamicbase.
|
||||
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
|
||||
}
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
@@ -313,7 +302,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// systems so we need separate assembly files.
|
||||
suffix = "_windows"
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ func (b *builder) createAlias(alias llvm.Value) {
|
||||
pos := b.program.Fset.Position(b.fn.Pos())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
||||
entryBlock := b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||
b.SetInsertPointAtEnd(entryBlock)
|
||||
if b.llvmFn.Type() != alias.Type() {
|
||||
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||
|
||||
+3
-1
@@ -240,8 +240,10 @@ 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")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next")
|
||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
|
||||
// Now branch to the out-of-bounds or the regular block.
|
||||
|
||||
+25
-48
@@ -4,19 +4,17 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
|
||||
// operation. It returns the result of the operation and true if the call could
|
||||
// be lowered inline, and false otherwise.
|
||||
func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
name := call.Value.(*ssa.Function).Name()
|
||||
// createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic
|
||||
// operation. It returns the result of the operation, or a zero llvm.Value if
|
||||
// the result is void.
|
||||
func (b *builder) createAtomicOp(name string) llvm.Value {
|
||||
switch name {
|
||||
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.getValue(b.fn.Params[1])
|
||||
if strings.HasPrefix(b.Triple, "avr") {
|
||||
// AtomicRMW does not work on AVR as intended:
|
||||
// - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release)
|
||||
@@ -29,17 +27,18 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
}
|
||||
oldVal := b.createCall(fn, []llvm.Value{ptr, val}, "")
|
||||
// Return the new value, not the original value returned.
|
||||
return b.CreateAdd(oldVal, val, ""), true
|
||||
return b.CreateAdd(oldVal, val, "")
|
||||
}
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
// Return the new value, not the original value returned by atomicrmw.
|
||||
return b.CreateAdd(oldVal, val, ""), true
|
||||
return b.CreateAdd(oldVal, val, "")
|
||||
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.getValue(b.fn.Params[1])
|
||||
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
|
||||
if isPointer {
|
||||
// atomicrmw only supports integers, so cast to an integer.
|
||||
// TODO: this is fixed in LLVM 15.
|
||||
val = b.CreatePtrToInt(val, b.uintptrType, "")
|
||||
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
|
||||
}
|
||||
@@ -47,47 +46,23 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
if isPointer {
|
||||
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
|
||||
}
|
||||
return oldVal, true
|
||||
return oldVal
|
||||
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
old := b.getValue(call.Args[1])
|
||||
newVal := b.getValue(call.Args[2])
|
||||
if strings.HasSuffix(name, "64") {
|
||||
if strings.HasPrefix(b.Triple, "thumb") {
|
||||
// Work around a bug in LLVM, at least LLVM 11:
|
||||
// https://reviews.llvm.org/D95891
|
||||
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
|
||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
||||
if compareAndSwap.IsNil() {
|
||||
// Declare the function if it isn't already declared.
|
||||
i64Type := b.ctx.Int64Type()
|
||||
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
|
||||
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
|
||||
}
|
||||
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
|
||||
// The __sync_val_compare_and_swap_8 function returns the old
|
||||
// value. However, we shouldn't return the old value, we should
|
||||
// return whether the compare/exchange was successful. This is
|
||||
// easily done by comparing the returned (actual) old value with
|
||||
// the expected old value passed to
|
||||
// __sync_val_compare_and_swap_8.
|
||||
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
|
||||
return swapped, true
|
||||
}
|
||||
}
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
old := b.getValue(b.fn.Params[1])
|
||||
newVal := b.getValue(b.fn.Params[2])
|
||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||
return swapped, true
|
||||
return swapped
|
||||
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.CreateLoad(ptr, "")
|
||||
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return val, true
|
||||
return val
|
||||
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.getValue(b.fn.Params[1])
|
||||
if strings.HasPrefix(b.Triple, "avr") {
|
||||
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
|
||||
vType := val.Type()
|
||||
@@ -103,13 +78,15 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
|
||||
}
|
||||
return b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, ""), true
|
||||
b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
|
||||
return llvm.Value{}
|
||||
}
|
||||
store := b.CreateStore(val, ptr)
|
||||
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return store, true
|
||||
return llvm.Value{}
|
||||
default:
|
||||
return llvm.Value{}, false
|
||||
b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name())
|
||||
return llvm.Value{}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-2
@@ -33,17 +33,36 @@ const (
|
||||
paramIsDeferenceableOrNull = 1 << iota
|
||||
)
|
||||
|
||||
// createCall creates a new call to runtime.<fnName> with the given arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
// createRuntimeInvoke instead.
|
||||
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
||||
llvmFn := b.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||
}
|
||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||
if isInvoke {
|
||||
return b.createInvoke(llvmFn, args, name)
|
||||
}
|
||||
return b.createCall(llvmFn, args, name)
|
||||
}
|
||||
|
||||
// createRuntimeCall creates a new call to runtime.<fnName> with the given
|
||||
// arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
return b.createRuntimeCallCommon(fnName, args, name, false)
|
||||
}
|
||||
|
||||
// createRuntimeInvoke creates a new call to runtime.<fnName> with the given
|
||||
// arguments. If the runtime call panics, control flow is diverted to the
|
||||
// landing pad block.
|
||||
// Note that "invoke" here is meant in the LLVM sense (a call that can
|
||||
// panic/throw), not in the Go sense (an interface method call).
|
||||
func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
return b.createRuntimeCallCommon(fnName, args, name, true)
|
||||
}
|
||||
|
||||
// createCall creates a call to the given function with the arguments possibly
|
||||
// expanded.
|
||||
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
@@ -55,6 +74,15 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
|
||||
return b.CreateCall(fn, expanded, name)
|
||||
}
|
||||
|
||||
// createInvoke is like createCall but continues execution at the landing pad if
|
||||
// the call resulted in a panic.
|
||||
func (b *builder) createInvoke(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
if b.hasDeferFrame() {
|
||||
b.createInvokeCheckpoint()
|
||||
}
|
||||
return b.createCall(fn, args, name)
|
||||
}
|
||||
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
// parameter list.
|
||||
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
|
||||
+246
-59
@@ -9,6 +9,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/bits"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -20,6 +21,10 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
var typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||
return t
|
||||
}
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
@@ -76,6 +81,7 @@ type compilerContext struct {
|
||||
program *ssa.Program
|
||||
diagnostics []error
|
||||
astComments map[string]*ast.CommentGroup
|
||||
embedGlobals map[string][]*loader.EmbedFile
|
||||
pkg *types.Package
|
||||
packageDir string // directory for this package
|
||||
runtimePkg *types.Package
|
||||
@@ -137,6 +143,8 @@ type builder struct {
|
||||
currentBlock *ssa.BasicBlock
|
||||
phis []phiNode
|
||||
deferPtr llvm.Value
|
||||
deferFrame llvm.Value
|
||||
landingpad llvm.BasicBlock
|
||||
difunc llvm.Metadata
|
||||
dilocals map[*types.Var]llvm.Metadata
|
||||
initInlinedAt llvm.Metadata // fake inlinedAt position
|
||||
@@ -250,6 +258,7 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
||||
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||
c.packageDir = pkg.OriginalDir()
|
||||
c.embedGlobals = pkg.EmbedGlobals
|
||||
c.pkg = pkg.Pkg
|
||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||
c.program = ssaPkg.Prog
|
||||
@@ -301,6 +310,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
}),
|
||||
)
|
||||
c.dibuilder.Finalize()
|
||||
c.dibuilder.Destroy()
|
||||
}
|
||||
|
||||
return c.mod, c.diagnostics
|
||||
@@ -331,6 +341,7 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||
// getLLVMType instead.
|
||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||
goType = typeParamUnderlyingType(goType)
|
||||
switch typ := goType.(type) {
|
||||
case *types.Array:
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
@@ -440,6 +451,7 @@ func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata {
|
||||
// createDIType creates a new DWARF type. Don't call this function directly,
|
||||
// call getDIType instead.
|
||||
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
||||
typ = typeParamUnderlyingType(typ)
|
||||
llvmType := c.getLLVMType(typ)
|
||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||
switch typ := typ.(type) {
|
||||
@@ -773,7 +785,12 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
if member.Blocks == nil {
|
||||
continue // external function
|
||||
// Try to define this as an intrinsic function.
|
||||
b.defineIntrinsicFunction()
|
||||
// It might not be an intrinsic function but simply an external
|
||||
// function (defined via //go:linkname). Leave it undefined in
|
||||
// that case.
|
||||
continue
|
||||
}
|
||||
b.createFunction()
|
||||
case *ssa.Type:
|
||||
@@ -790,6 +807,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
for _, method := range methods {
|
||||
// Parse this method.
|
||||
fn := pkg.Prog.MethodValue(method)
|
||||
if fn == nil {
|
||||
continue // probably a generic method
|
||||
}
|
||||
if fn.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
@@ -814,7 +834,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
// Global variable.
|
||||
info := c.getGlobalInfo(member)
|
||||
global := c.getGlobal(member)
|
||||
if !info.extern {
|
||||
if files, ok := c.embedGlobals[member.Name()]; ok {
|
||||
c.createEmbedGlobal(member, global, files)
|
||||
} else if !info.extern {
|
||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
if info.section != "" {
|
||||
@@ -848,10 +870,155 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
}
|
||||
}
|
||||
|
||||
// createFunction builds the LLVM IR implementation for this function. The
|
||||
// function must not yet be defined, otherwise this function will create a
|
||||
// diagnostic.
|
||||
func (b *builder) createFunction() {
|
||||
// createEmbedGlobal creates an initializer for a //go:embed global variable.
|
||||
func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
|
||||
switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
// String type.
|
||||
if typ.Kind() != types.String {
|
||||
// This is checked at the AST level, so should be unreachable.
|
||||
panic("expected a string type")
|
||||
}
|
||||
if len(files) != 1 {
|
||||
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||
return
|
||||
}
|
||||
strObj := c.getEmbedFileString(files[0])
|
||||
global.SetInitializer(strObj)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
|
||||
case *types.Slice:
|
||||
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
||||
// This is checked at the AST level, so should be unreachable.
|
||||
panic("expected a byte slice")
|
||||
}
|
||||
if len(files) != 1 {
|
||||
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||
return
|
||||
}
|
||||
file := files[0]
|
||||
bufferValue := c.ctx.ConstString(string(file.Data), false)
|
||||
bufferGlobal := llvm.AddGlobal(c.mod, bufferValue.Type(), c.pkg.Path()+"$embedslice")
|
||||
bufferGlobal.SetInitializer(bufferValue)
|
||||
bufferGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
bufferGlobal.SetAlignment(1)
|
||||
slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
})
|
||||
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||
global.SetInitializer(sliceObj)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
|
||||
case *types.Struct:
|
||||
// Assume this is an embed.FS struct:
|
||||
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/embed/embed.go;l=148
|
||||
// It looks like this:
|
||||
// type FS struct {
|
||||
// files *file
|
||||
// }
|
||||
|
||||
// Make a slice of the files, as they will appear in the binary. They
|
||||
// are sorted in a special way to allow for binary searches, see
|
||||
// src/embed/embed.go for details.
|
||||
dirset := map[string]struct{}{}
|
||||
var allFiles []*loader.EmbedFile
|
||||
for _, file := range files {
|
||||
allFiles = append(allFiles, file)
|
||||
dirname := file.Name
|
||||
for {
|
||||
dirname, _ = path.Split(path.Clean(dirname))
|
||||
if dirname == "" {
|
||||
break
|
||||
}
|
||||
if _, ok := dirset[dirname]; ok {
|
||||
break
|
||||
}
|
||||
dirset[dirname] = struct{}{}
|
||||
allFiles = append(allFiles, &loader.EmbedFile{
|
||||
Name: dirname,
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.Slice(allFiles, func(i, j int) bool {
|
||||
dir1, name1 := path.Split(path.Clean(allFiles[i].Name))
|
||||
dir2, name2 := path.Split(path.Clean(allFiles[j].Name))
|
||||
if dir1 != dir2 {
|
||||
return dir1 < dir2
|
||||
}
|
||||
return name1 < name2
|
||||
})
|
||||
|
||||
// Make the backing array for the []files slice. This is a LLVM global.
|
||||
embedFileStructType := c.getLLVMType(typ.Field(0).Type().(*types.Pointer).Elem().(*types.Slice).Elem())
|
||||
var fileStructs []llvm.Value
|
||||
for _, file := range allFiles {
|
||||
fileStruct := llvm.ConstNull(embedFileStructType)
|
||||
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
|
||||
fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
|
||||
if file.Hash != "" {
|
||||
data := c.getEmbedFileString(file)
|
||||
fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
|
||||
}
|
||||
fileStructs = append(fileStructs, fileStruct)
|
||||
}
|
||||
sliceDataInitializer := llvm.ConstArray(embedFileStructType, fileStructs)
|
||||
sliceDataGlobal := llvm.AddGlobal(c.mod, sliceDataInitializer.Type(), c.pkg.Path()+"$embedfsfiles")
|
||||
sliceDataGlobal.SetInitializer(sliceDataInitializer)
|
||||
sliceDataGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
sliceDataGlobal.SetGlobalConstant(true)
|
||||
sliceDataGlobal.SetUnnamedAddr(true)
|
||||
sliceDataGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceDataInitializer.Type()))
|
||||
|
||||
// Create the slice object itself.
|
||||
// Because embed.FS refers to it as *[]embed.file instead of a plain
|
||||
// []embed.file, we have to store this as a global.
|
||||
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
})
|
||||
sliceLen := llvm.ConstInt(c.uintptrType, uint64(len(fileStructs)), false)
|
||||
sliceInitializer := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||
sliceGlobal := llvm.AddGlobal(c.mod, sliceInitializer.Type(), c.pkg.Path()+"$embedfsslice")
|
||||
sliceGlobal.SetInitializer(sliceInitializer)
|
||||
sliceGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
sliceGlobal.SetGlobalConstant(true)
|
||||
sliceGlobal.SetUnnamedAddr(true)
|
||||
sliceGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceInitializer.Type()))
|
||||
|
||||
// Define the embed.FS struct. It has only one field: the files (as a
|
||||
// *[]embed.file).
|
||||
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
||||
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
|
||||
global.SetInitializer(globalInitializer)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
// getEmbedFileString returns the (constant) string object with the contents of
|
||||
// the given file. This is a llvm.Value of a regular Go string.
|
||||
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
|
||||
dataGlobalName := "embed/file_" + file.Hash
|
||||
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
|
||||
if dataGlobal.IsNil() {
|
||||
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
|
||||
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
|
||||
}
|
||||
strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
})
|
||||
strLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
}
|
||||
|
||||
// Start defining a function so that it can be filled with instructions: load
|
||||
// parameters, create basic blocks, and set up debug information.
|
||||
// This is separated out from createFunction() so that it is also usable to
|
||||
// define compiler intrinsics like the atomic operations in sync/atomic.
|
||||
func (b *builder) createFunctionStart() {
|
||||
if b.DumpSSA {
|
||||
fmt.Printf("\nfunc %s:\n", b.fn)
|
||||
}
|
||||
@@ -921,7 +1088,16 @@ func (b *builder) createFunction() {
|
||||
b.blockEntries[block] = llvmBlock
|
||||
b.blockExits[block] = llvmBlock
|
||||
}
|
||||
entryBlock := b.blockEntries[b.fn.Blocks[0]]
|
||||
var entryBlock llvm.BasicBlock
|
||||
if len(b.fn.Blocks) != 0 {
|
||||
// Normal functions have an entry block.
|
||||
entryBlock = b.blockEntries[b.fn.Blocks[0]]
|
||||
} else {
|
||||
// This function isn't defined in Go SSA. It is probably a compiler
|
||||
// intrinsic (like an atomic operation). Create the entry block
|
||||
// manually.
|
||||
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||
}
|
||||
b.SetInsertPointAtEnd(entryBlock)
|
||||
|
||||
if b.fn.Synthetic == "package initializer" {
|
||||
@@ -996,6 +1172,13 @@ func (b *builder) createFunction() {
|
||||
// them.
|
||||
b.deferInitFunc()
|
||||
}
|
||||
}
|
||||
|
||||
// createFunction builds the LLVM IR implementation for this function. The
|
||||
// function must not yet be defined, otherwise this function will create a
|
||||
// diagnostic.
|
||||
func (b *builder) createFunction() {
|
||||
b.createFunctionStart()
|
||||
|
||||
// Fill blocks with instructions.
|
||||
for _, block := range b.fn.DomPreorder() {
|
||||
@@ -1043,6 +1226,12 @@ func (b *builder) createFunction() {
|
||||
}
|
||||
}
|
||||
|
||||
if b.hasDeferFrame() {
|
||||
// Create the landing pad block, where execution continues after a
|
||||
// panic.
|
||||
b.createLandingPad()
|
||||
}
|
||||
|
||||
// Resolve phi nodes
|
||||
for _, phi := range b.phis {
|
||||
block := phi.ssa.Block()
|
||||
@@ -1170,9 +1359,12 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
|
||||
case *ssa.Panic:
|
||||
value := b.getValue(instr.X)
|
||||
b.createRuntimeCall("_panic", []llvm.Value{value}, "")
|
||||
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
|
||||
b.CreateUnreachable()
|
||||
case *ssa.Return:
|
||||
if b.hasDeferFrame() {
|
||||
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
|
||||
}
|
||||
if len(instr.Results) == 0 {
|
||||
b.CreateRetVoid()
|
||||
} else if len(instr.Results) == 1 {
|
||||
@@ -1361,7 +1553,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
cplx := argValues[0]
|
||||
return b.CreateExtractValue(cplx, 0, "real"), nil
|
||||
case "recover":
|
||||
return b.createRuntimeCall("_recover", nil, ""), nil
|
||||
useParentFrame := uint64(0)
|
||||
if b.hasDeferFrame() {
|
||||
// recover() should return the panic value of the parent function,
|
||||
// not of the current function.
|
||||
useParentFrame = 1
|
||||
}
|
||||
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
|
||||
case "ssa:wrapnilchk":
|
||||
// TODO: do an actual nil check?
|
||||
return argValues[0], nil
|
||||
@@ -1421,10 +1619,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
// applied) function call. If it is anonymous, it may be a closure.
|
||||
name := fn.RelString(nil)
|
||||
switch {
|
||||
case name == "runtime.memcpy" || name == "runtime.memmove" || name == "reflect.memcpy":
|
||||
return b.createMemoryCopyCall(fn, instr.Args)
|
||||
case name == "runtime.memzero":
|
||||
return b.createMemoryZeroCall(instr.Args)
|
||||
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
|
||||
result, ok := b.createMathOp(instr)
|
||||
if ok {
|
||||
@@ -1444,18 +1638,12 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.createSyscall(instr)
|
||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
||||
return b.createRawSyscallNoError(instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
return b.createVolatileLoad(instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
return b.createVolatileStore(instr)
|
||||
case strings.HasPrefix(name, "sync/atomic."):
|
||||
val, ok := b.createAtomicOp(instr)
|
||||
if ok {
|
||||
// This call could be lowered as an atomic operation.
|
||||
return val, nil
|
||||
case name == "runtime.supportsRecover":
|
||||
supportsRecover := uint64(0)
|
||||
if b.supportsRecover() {
|
||||
supportsRecover = 1
|
||||
}
|
||||
// This call couldn't be lowered as an atomic operation, it's
|
||||
// probably something else. Continue as usual.
|
||||
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
|
||||
case name == "runtime/interrupt.New":
|
||||
return b.createInterruptGlobal(instr)
|
||||
}
|
||||
@@ -1518,7 +1706,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
params = append(params, context)
|
||||
}
|
||||
|
||||
return b.createCall(callee, params, ""), nil
|
||||
return b.createInvoke(callee, params, ""), nil
|
||||
}
|
||||
|
||||
// getValue returns the LLVM value of a constant, function value, global, or
|
||||
@@ -2469,42 +2657,41 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
|
||||
}
|
||||
|
||||
// createConst creates a LLVM constant value from a Go constant.
|
||||
func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
||||
func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
||||
switch typ := expr.Type().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
llvmType := b.getLLVMType(typ)
|
||||
llvmType := c.getLLVMType(typ)
|
||||
if typ.Info()&types.IsBoolean != 0 {
|
||||
b := constant.BoolVal(expr.Value)
|
||||
n := uint64(0)
|
||||
if b {
|
||||
if constant.BoolVal(expr.Value) {
|
||||
n = 1
|
||||
}
|
||||
return llvm.ConstInt(llvmType, n, false)
|
||||
} else if typ.Info()&types.IsString != 0 {
|
||||
str := constant.StringVal(expr.Value)
|
||||
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
||||
strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false)
|
||||
var strPtr llvm.Value
|
||||
if str != "" {
|
||||
objname := b.pkg.Path() + "$string"
|
||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||
objname := c.pkg.Path() + "$string"
|
||||
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(c.ctx.ConstString(str, false))
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetAlignment(1)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
|
||||
} else {
|
||||
strPtr = llvm.ConstNull(b.i8ptrType)
|
||||
strPtr = llvm.ConstNull(c.i8ptrType)
|
||||
}
|
||||
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
if !expr.IsNil() {
|
||||
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
|
||||
}
|
||||
return llvm.ConstNull(b.i8ptrType)
|
||||
return llvm.ConstNull(c.i8ptrType)
|
||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||
return llvm.ConstInt(llvmType, n, false)
|
||||
@@ -2515,18 +2702,18 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
||||
n, _ := constant.Float64Val(expr.Value)
|
||||
return llvm.ConstFloat(llvmType, n)
|
||||
} else if typ.Kind() == types.Complex64 {
|
||||
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||
return cplx
|
||||
} else if typ.Kind() == types.Complex128 {
|
||||
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||
return cplx
|
||||
} else {
|
||||
panic("unknown constant of basic type: " + expr.String())
|
||||
@@ -2535,35 +2722,35 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
||||
if expr.Value != nil {
|
||||
panic("expected nil chan constant")
|
||||
}
|
||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||
case *types.Signature:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil signature constant")
|
||||
}
|
||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||
case *types.Interface:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil interface constant")
|
||||
}
|
||||
// Create a generic nil interface with no dynamic type (typecode=0).
|
||||
fields := []llvm.Value{
|
||||
llvm.ConstInt(b.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(b.i8ptrType),
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(c.i8ptrType),
|
||||
}
|
||||
return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
|
||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||
case *types.Pointer:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil pointer constant")
|
||||
}
|
||||
return llvm.ConstPointerNull(b.getLLVMType(typ))
|
||||
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
||||
case *types.Slice:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil slice constant")
|
||||
}
|
||||
elemType := b.getLLVMType(typ.Elem())
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
||||
llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
|
||||
slice := b.ctx.ConstStruct([]llvm.Value{
|
||||
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||
llvmPtr, // backing array
|
||||
llvmLen, // len
|
||||
llvmLen, // cap
|
||||
@@ -2574,7 +2761,7 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
||||
// I believe this is not allowed by the Go spec.
|
||||
panic("non-nil map constant")
|
||||
}
|
||||
llvmType := b.getLLVMType(typ)
|
||||
llvmType := c.getLLVMType(typ)
|
||||
return llvm.ConstNull(llvmType)
|
||||
default:
|
||||
panic("unknown constant: " + expr.String())
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package compiler
|
||||
|
||||
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||
// support.
|
||||
|
||||
import "go/types"
|
||||
|
||||
func init() {
|
||||
typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||
if t, ok := t.(*types.TypeParam); ok {
|
||||
return t.Underlying()
|
||||
}
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -28,18 +28,11 @@ type testCase struct {
|
||||
func TestCompiler(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Check LLVM version.
|
||||
// Determine LLVM version.
|
||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
if err != nil {
|
||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||
}
|
||||
if llvmMajor < 11 {
|
||||
// It is likely this version needs to be bumped in the future.
|
||||
// The goal is to at least test the LLVM version that's used by default
|
||||
// in TinyGo and (if possible without too many workarounds) also some
|
||||
// previous versions.
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
@@ -56,6 +49,7 @@ func TestCompiler(t *testing.T) {
|
||||
{"float.go", "", ""},
|
||||
{"interface.go", "", ""},
|
||||
{"func.go", "", ""},
|
||||
{"defer.go", "cortex-m-qemu", ""},
|
||||
{"pragma.go", "", ""},
|
||||
{"goroutine.go", "wasm", "asyncify"},
|
||||
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
||||
@@ -100,6 +94,7 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
compilerConfig := &Config{
|
||||
Triple: config.Triple(),
|
||||
Features: config.Features(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
@@ -113,9 +108,10 @@ func TestCompiler(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, "./testdata/"+tc.file, config.ClangHeaders, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -221,11 +217,6 @@ func filterIrrelevantIRLines(lines []string) []string {
|
||||
if strings.HasPrefix(line, "source_filename = ") {
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
|
||||
// Ignore attribute groups. These may change between LLVM versions.
|
||||
// Right now test outputs are for LLVM 12 and higher.
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
|
||||
// The datalayout string may vary betewen LLVM versions.
|
||||
// Right now test outputs are for LLVM 14 and higher.
|
||||
|
||||
+191
-28
@@ -15,12 +15,39 @@ package compiler
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// supportsRecover returns whether the compiler supports the recover() builtin
|
||||
// for the current architecture.
|
||||
func (b *builder) supportsRecover() bool {
|
||||
switch b.archFamily() {
|
||||
case "wasm32":
|
||||
// Probably needs to be implemented using the exception handling
|
||||
// proposal of WebAssembly:
|
||||
// https://github.com/WebAssembly/exception-handling
|
||||
return false
|
||||
case "riscv64", "xtensa":
|
||||
// TODO: add support for these architectures
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// hasDeferFrame returns whether the current function needs to catch panics and
|
||||
// run defers.
|
||||
func (b *builder) hasDeferFrame() bool {
|
||||
if b.fn.Recover == nil {
|
||||
return false
|
||||
}
|
||||
return b.supportsRecover()
|
||||
}
|
||||
|
||||
// deferInitFunc sets up this function for future deferred calls. It must be
|
||||
// called from within the entry block when this function contains deferred
|
||||
// calls.
|
||||
@@ -36,6 +63,141 @@ func (b *builder) deferInitFunc() {
|
||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
||||
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(deferType), 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
|
||||
// function prologue/epilogue (an invariant maintained by TinyGo but
|
||||
// possibly broken by the C alloca function).
|
||||
// The frame pointer is _not_ saved, because it is marked as clobbered
|
||||
// in the setjmp-like inline assembly.
|
||||
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// createLandingPad fills in the landing pad block. This block runs the deferred
|
||||
// functions and returns (by jumping to the recover block). If the function is
|
||||
// still panicking after the defers are run, the panic will be re-raised in
|
||||
// destroyDeferFrame.
|
||||
func (b *builder) createLandingPad() {
|
||||
b.SetInsertPointAtEnd(b.landingpad)
|
||||
|
||||
// Add debug info, if needed.
|
||||
// The location used is the closing bracket of the function.
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(b.fn.Syntax().End())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
b.createRunDefers()
|
||||
|
||||
// Continue at the 'recover' block, which returns to the parent in an
|
||||
// appropriate way.
|
||||
b.CreateBr(b.blockEntries[b.fn.Recover])
|
||||
}
|
||||
|
||||
// createInvokeCheckpoint saves the function state at the given point, to
|
||||
// continue at the landing pad if a panic happened. This is implemented using a
|
||||
// setjmp-like construct.
|
||||
func (b *builder) createInvokeCheckpoint() {
|
||||
// Construct inline assembly equivalents of setjmp.
|
||||
// The assembly works as follows:
|
||||
// * All registers (both callee-saved and caller saved) are clobbered
|
||||
// after the inline assembly returns.
|
||||
// * The assembly stores the address just past the end of the assembly
|
||||
// into the jump buffer.
|
||||
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
||||
// assembly but set to an unspecified non-zero value when jumping using
|
||||
// a longjmp.
|
||||
var asmString, constraints string
|
||||
resultType := b.uintptrType
|
||||
switch b.archFamily() {
|
||||
case "i386":
|
||||
asmString = `
|
||||
xorl %eax, %eax
|
||||
movl $$1f, 4(%ebx)
|
||||
1:`
|
||||
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
// This doesn't include the floating point stack because TinyGo uses
|
||||
// newer floating point instructions.
|
||||
case "x86_64":
|
||||
asmString = `
|
||||
leaq 1f(%rip), %rax
|
||||
movq %rax, 8(%rbx)
|
||||
xorq %rax, %rax
|
||||
1:`
|
||||
constraints = "={rax},{rbx},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
// This list doesn't include AVX/AVX512 registers because TinyGo
|
||||
// doesn't currently enable support for AVX instructions.
|
||||
case "arm":
|
||||
// Note: the following assembly takes into account that the PC is
|
||||
// always 4 bytes ahead on ARM. The PC that is stored always points
|
||||
// to the instruction just after the assembly fragment so that
|
||||
// tinygo_longjmp lands at the correct instruction.
|
||||
if b.isThumb() {
|
||||
// Instructions are 2 bytes in size.
|
||||
asmString = `
|
||||
movs r0, #0
|
||||
mov r2, pc
|
||||
str r2, [r1, #4]`
|
||||
} else {
|
||||
// Instructions are 4 bytes in size.
|
||||
asmString = `
|
||||
str pc, [r1, #4]
|
||||
movs r0, #0`
|
||||
}
|
||||
constraints = "={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}"
|
||||
case "aarch64":
|
||||
asmString = `
|
||||
adr x2, 1f
|
||||
str x2, [x1, #8]
|
||||
mov x0, #0
|
||||
1:
|
||||
`
|
||||
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{fp},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
|
||||
// TODO: SVE registers, which we don't use in TinyGo at the moment.
|
||||
case "avr":
|
||||
// Note: the Y register (R28:R29) is a fixed register and therefore
|
||||
// needs to be saved manually. TODO: do this only once per function with
|
||||
// a defer frame, not for every call.
|
||||
resultType = b.ctx.Int8Type()
|
||||
asmString = `
|
||||
ldi r24, pm_lo8(1f)
|
||||
ldi r25, pm_hi8(1f)
|
||||
std z+2, r24
|
||||
std z+3, r25
|
||||
std z+4, r28
|
||||
std z+5, r29
|
||||
ldi r24, 0
|
||||
1:`
|
||||
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
|
||||
case "riscv32":
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
sw a2, 4(a1)
|
||||
li a0, 0
|
||||
1:`
|
||||
constraints = "={a0},{a1},~{a1},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{s0},~{s1},~{s2},~{s3},~{s4},~{s5},~{s6},~{s7},~{s8},~{s9},~{s10},~{s11},~{t0},~{t1},~{t2},~{t3},~{t4},~{t5},~{t6},~{ra},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{memory}"
|
||||
default:
|
||||
// This case should have been handled by b.supportsRecover().
|
||||
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
|
||||
}
|
||||
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
||||
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||
result := b.CreateCall(asm, []llvm.Value{b.deferFrame}, "setjmp")
|
||||
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
||||
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
b.blockExits[b.currentBlock] = continueBB
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
@@ -201,30 +363,31 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
}
|
||||
}
|
||||
|
||||
// Make a struct out of the collected values to put in the defer frame.
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFrame := llvm.ConstNull(deferFrameType)
|
||||
// Make a struct out of the collected values to put in the deferred call
|
||||
// struct.
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCall := llvm.ConstNull(deferredCallType)
|
||||
for i, value := range values {
|
||||
deferFrame = b.CreateInsertValue(deferFrame, value, i, "")
|
||||
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
||||
}
|
||||
|
||||
// Put this struct in an allocation.
|
||||
var alloca llvm.Value
|
||||
if !isInLoop(instr.Block()) {
|
||||
// This can safely use a stack allocation.
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca")
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
||||
} else {
|
||||
// This may be hit a variable number of times, so use a heap allocation.
|
||||
size := b.targetData.TypeAllocSize(deferFrameType)
|
||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
nilPtr := llvm.ConstNull(b.i8ptrType)
|
||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferredCallType, 0), "defer.alloc")
|
||||
}
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(alloca)
|
||||
}
|
||||
b.CreateStore(deferFrame, alloca)
|
||||
b.CreateStore(deferredCall, alloca)
|
||||
|
||||
// Push it on top of the linked list by replacing deferPtr.
|
||||
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||
@@ -248,11 +411,11 @@ func (b *builder) createRunDefers() {
|
||||
// }
|
||||
// }
|
||||
|
||||
// Create loop.
|
||||
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
|
||||
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
|
||||
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
|
||||
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
|
||||
// Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end.
|
||||
end := b.insertBasicBlock("rundefers.end")
|
||||
unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default")
|
||||
loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop")
|
||||
loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead")
|
||||
b.CreateBr(loophead)
|
||||
|
||||
// Create loop head:
|
||||
@@ -284,7 +447,7 @@ func (b *builder) createRunDefers() {
|
||||
// Create switch case, for example:
|
||||
// case 0:
|
||||
// // run first deferred call
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
|
||||
block := b.insertBasicBlock("rundefers.callback" + strconv.Itoa(i))
|
||||
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
|
||||
b.SetInsertPointAtEnd(block)
|
||||
switch callback := callback.(type) {
|
||||
@@ -295,7 +458,7 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
|
||||
if !callback.IsInvoke() {
|
||||
//Expect funcValue to be passed through the defer frame.
|
||||
//Expect funcValue to be passed through the deferred call.
|
||||
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
||||
} else {
|
||||
//Expect typecode
|
||||
@@ -306,14 +469,14 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||
}
|
||||
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct (including receiver).
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 2; i < len(valueTypes); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
@@ -353,14 +516,14 @@ func (b *builder) createRunDefers() {
|
||||
for _, param := range getParams(callback.Signature) {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||
}
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct.
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := range getParams(callback.Signature) {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
@@ -374,7 +537,7 @@ func (b *builder) createRunDefers() {
|
||||
}
|
||||
|
||||
// Call real function.
|
||||
b.createCall(b.getFunction(callback), forwardParams, "")
|
||||
b.createInvoke(b.getFunction(callback), forwardParams, "")
|
||||
|
||||
case *ssa.MakeClosure:
|
||||
// Get the real defer struct type and cast to it.
|
||||
@@ -385,14 +548,14 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
valueTypes = append(valueTypes, b.i8ptrType) // closure
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct.
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 2; i < len(valueTypes); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
@@ -411,14 +574,14 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct.
|
||||
var argValues []llvm.Value
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
argValues = append(argValues, forwardParam)
|
||||
}
|
||||
|
||||
@@ -389,8 +389,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
// value.
|
||||
|
||||
prevBlock := b.GetInsertBlock()
|
||||
okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.next")
|
||||
okBlock := b.insertBasicBlock("typeassert.ok")
|
||||
nextBlock := b.insertBasicBlock("typeassert.next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
|
||||
|
||||
+42
-10
@@ -3,36 +3,68 @@ package compiler
|
||||
// This file contains helper functions to create calls to LLVM intrinsics.
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createMemoryCopyCall creates a call to a builtin LLVM memcpy or memmove
|
||||
// Define unimplemented intrinsic functions.
|
||||
//
|
||||
// Some functions are either normally implemented in Go assembly (like
|
||||
// sync/atomic functions) or intentionally left undefined to be implemented
|
||||
// directly in the compiler (like runtime/volatile functions). Either way, look
|
||||
// for these and implement them if this is the case.
|
||||
func (b *builder) defineIntrinsicFunction() {
|
||||
name := b.fn.RelString(nil)
|
||||
switch {
|
||||
case name == "runtime.memcpy" || name == "runtime.memmove":
|
||||
b.createMemoryCopyImpl()
|
||||
case name == "runtime.memzero":
|
||||
b.createMemoryZeroImpl()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
b.createVolatileLoad()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
b.createVolatileStore()
|
||||
case strings.HasPrefix(name, "sync/atomic.") && token.IsExported(b.fn.Name()):
|
||||
b.createFunctionStart()
|
||||
returnValue := b.createAtomicOp(b.fn.Name())
|
||||
if !returnValue.IsNil() {
|
||||
b.CreateRet(returnValue)
|
||||
} else {
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createMemoryCopyImpl creates a call to a builtin LLVM memcpy or memmove
|
||||
// function, declaring this function if needed. These calls are treated
|
||||
// specially by optimization passes possibly resulting in better generated code,
|
||||
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
||||
func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm.Value, error) {
|
||||
fnName := "llvm." + fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
func (b *builder) createMemoryCopyImpl() {
|
||||
b.createFunctionStart()
|
||||
fnName := "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
var params []llvm.Value
|
||||
for _, param := range args {
|
||||
for _, param := range b.fn.Params {
|
||||
params = append(params, b.getValue(param))
|
||||
}
|
||||
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
return llvm.Value{}, nil
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// createMemoryZeroCall creates calls to llvm.memset.* to zero a block of
|
||||
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
||||
// memory, declaring the function if needed. These calls will be lowered to
|
||||
// regular libc memset calls if they aren't optimized out in a different way.
|
||||
func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
||||
func (b *builder) createMemoryZeroImpl() {
|
||||
b.createFunctionStart()
|
||||
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
@@ -40,13 +72,13 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
params := []llvm.Value{
|
||||
b.getValue(args[0]),
|
||||
b.getValue(b.fn.Params[0]),
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||
b.getValue(args[1]),
|
||||
b.getValue(b.fn.Params[1]),
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||
}
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
return llvm.Value{}, nil
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -23,6 +24,23 @@ func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitca
|
||||
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
||||
}
|
||||
|
||||
// insertBasicBlock inserts a new basic block after the current basic block.
|
||||
// This is useful when inserting new basic blocks while converting a
|
||||
// *ssa.BasicBlock to a llvm.BasicBlock and the LLVM basic block needs some
|
||||
// extra blocks.
|
||||
// It does not update b.blockExits, this must be done by the caller.
|
||||
func (b *builder) insertBasicBlock(name string) llvm.BasicBlock {
|
||||
currentBB := b.Builder.GetInsertBlock()
|
||||
nextBB := llvm.NextBasicBlock(currentBB)
|
||||
if nextBB.IsNil() {
|
||||
// Last basic block in the function, so add one to the end.
|
||||
return b.ctx.AddBasicBlock(b.llvmFn, name)
|
||||
}
|
||||
// Insert a basic block before the next basic block - that is, at the
|
||||
// current insert location.
|
||||
return b.ctx.InsertBasicBlock(nextBB, name)
|
||||
}
|
||||
|
||||
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||
// createTemporaryAlloca.
|
||||
@@ -253,3 +271,43 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
|
||||
panic("unknown LLVM type")
|
||||
}
|
||||
}
|
||||
|
||||
// archFamily returns the archtecture from the LLVM triple but with some
|
||||
// architecture names ("armv6", "thumbv7m", etc) merged into a single
|
||||
// architecture name ("arm").
|
||||
func (c *compilerContext) archFamily() string {
|
||||
arch := strings.Split(c.Triple, "-")[0]
|
||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||
return "arm"
|
||||
}
|
||||
return arch
|
||||
}
|
||||
|
||||
// isThumb returns whether we're in ARM or in Thumb mode. It panics if the
|
||||
// features string is not one for an ARM architecture.
|
||||
func (c *compilerContext) isThumb() bool {
|
||||
var isThumb, isNotThumb bool
|
||||
for _, feature := range strings.Split(c.Features, ",") {
|
||||
if feature == "+thumb-mode" {
|
||||
isThumb = true
|
||||
}
|
||||
if feature == "-thumb-mode" {
|
||||
isNotThumb = true
|
||||
}
|
||||
}
|
||||
if isThumb == isNotThumb {
|
||||
panic("unexpected feature flags")
|
||||
}
|
||||
return isThumb
|
||||
}
|
||||
|
||||
// readStackPointer emits a LLVM intrinsic call that returns the current stack
|
||||
// pointer as an *i8.
|
||||
func (b *builder) readStackPointer() llvm.Value {
|
||||
stacksave := b.mod.NamedFunction("llvm.stacksave")
|
||||
if stacksave.IsNil() {
|
||||
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
|
||||
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
|
||||
}
|
||||
return b.CreateCall(stacksave, nil, "")
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
|
||||
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||
ctx := t.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||
@@ -46,6 +47,7 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
||||
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
|
||||
alloca := CreateEntryBlockAlloca(builder, t, name)
|
||||
|
||||
@@ -15,8 +15,9 @@ import (
|
||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||
|
||||
valueTypes := make([]llvm.Type, len(values))
|
||||
for i, value := range values {
|
||||
@@ -127,8 +128,9 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needs
|
||||
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||
|
||||
packedType := ctx.StructType(valueTypes, false)
|
||||
|
||||
|
||||
@@ -45,7 +45,11 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
||||
if t.Info()&types.IsString != 0 {
|
||||
return s.PtrSize
|
||||
}
|
||||
case *types.Signature:
|
||||
// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
|
||||
return s.PtrSize
|
||||
}
|
||||
|
||||
a := s.Sizeof(T) // may be 0
|
||||
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||
if a < 1 {
|
||||
|
||||
+1
-1
@@ -191,7 +191,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
// should be created right away.
|
||||
// The exception is the package initializer, which does appear in the
|
||||
// *ssa.Package members and so shouldn't be created here.
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" {
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
b := newBuilder(c, irbuilder, fn)
|
||||
b.createFunction()
|
||||
|
||||
+1
-34
@@ -16,20 +16,7 @@ import (
|
||||
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num := b.getValue(call.Args[0])
|
||||
switch {
|
||||
case b.GOARCH == "amd64":
|
||||
if b.GOOS == "darwin" {
|
||||
// Darwin adds this magic number to system call numbers:
|
||||
//
|
||||
// > Syscall classes for 64-bit system call entry.
|
||||
// > For 64-bit users, the 32-bit syscall number is partitioned
|
||||
// > with the high-order bits representing the class and low-order
|
||||
// > bits being the syscall number within that class.
|
||||
// > The high-order 32-bits of the 64-bit syscall number are unused.
|
||||
// > All system classes enter the kernel via the syscall instruction.
|
||||
//
|
||||
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
||||
num = b.CreateOr(num, llvm.ConstInt(b.uintptrType, 0x2000000, false), "")
|
||||
}
|
||||
case b.GOARCH == "amd64" && b.GOOS == "linux":
|
||||
// Sources:
|
||||
// https://stackoverflow.com/a/2538212
|
||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||
@@ -179,26 +166,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
case "darwin":
|
||||
syscallResult, err := b.createRawSyscall(call)
|
||||
if err != nil {
|
||||
return syscallResult, err
|
||||
}
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
// if syscallResult != 0 {
|
||||
// err = syscallResult
|
||||
// }
|
||||
// return syscallResult, 0, err
|
||||
zero := llvm.ConstInt(b.uintptrType, 0, false)
|
||||
hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
|
||||
errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError")
|
||||
retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
|
||||
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
case "windows":
|
||||
// On Windows, syscall.Syscall* is basically just a function pointer
|
||||
// call. This is complicated in gc because of stack switching and the
|
||||
|
||||
Vendored
+42
-40
@@ -6,40 +6,36 @@ target triple = "wasm32-unknown-wasi"
|
||||
%main.kv = type { float }
|
||||
%main.kv.0 = type { i8 }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = icmp eq i32 %y, -1
|
||||
%2 = icmp eq i32 %x, -2147483648
|
||||
@@ -47,35 +43,35 @@ divbyzero.next: ; preds = %entry
|
||||
%4 = select i1 %3, i32 1, i32 %y
|
||||
%5 = sdiv i32 %x, %4
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.divideByZeroPanic(i8*)
|
||||
declare void @runtime.divideByZeroPanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = udiv i32 %x, %y
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = icmp eq i32 %y, -1
|
||||
%2 = icmp eq i32 %x, -2147483648
|
||||
@@ -83,79 +79,83 @@ divbyzero.next: ; preds = %entry
|
||||
%4 = select i1 %3, i32 1, i32 %y
|
||||
%5 = srem i32 %x, %4
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = urem i32 %x, %y
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatEQ(float %x, float %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatNE(float %x, float %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatLower(float %x, float %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatGreater(float %x, float %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float %x.r
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context) unnamed_addr #0 {
|
||||
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fadd float %x.r, %y.r
|
||||
%1 = fadd float %x.i, %y.i
|
||||
@@ -165,7 +165,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
|
||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fsub float %x.r, %y.r
|
||||
%1 = fsub float %x.i, %y.i
|
||||
@@ -175,7 +175,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
|
||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fmul float %x.r, %y.r
|
||||
%1 = fmul float %x.i, %y.i
|
||||
@@ -189,16 +189,18 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @"main.foo$1"(%main.kv.0* null, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+24
-22
@@ -5,24 +5,24 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
||||
%"internal/task.gcData" = type { i8* }
|
||||
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
||||
%"internal/task.stackState" = type { i32, i32 }
|
||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
@@ -31,22 +31,22 @@ entry:
|
||||
store i32 3, i32* %chan.value, align 4
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
||||
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
|
||||
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2
|
||||
|
||||
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*)
|
||||
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
|
||||
|
||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
||||
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
|
||||
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
@@ -54,41 +54,41 @@ entry:
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*)
|
||||
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%complit = alloca {}, align 8
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%0 = bitcast {}* %complit to i8*
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #3
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||
%select.send.value = alloca i32, align 4
|
||||
@@ -105,7 +105,7 @@ entry:
|
||||
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
|
||||
store i8* null, i8** %.repack4, align 4
|
||||
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
|
||||
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef) #0
|
||||
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef) #3
|
||||
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||
%2 = icmp eq i32 %1, 0
|
||||
@@ -122,7 +122,9 @@ select.body: ; preds = %select.next
|
||||
br label %select.done
|
||||
}
|
||||
|
||||
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*)
|
||||
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*) #0
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { argmemonly nofree nosync nounwind willreturn }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { argmemonly nofree nosync nounwind willreturn }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
; ModuleID = 'defer.go'
|
||||
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._defer = type { i32, %runtime._defer* }
|
||||
%runtime.deferFrame = type { i8*, i8*, [0 x i8*], %runtime.deferFrame*, i1, %runtime._interface }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.external(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferSimple(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%defer.alloca = alloca { i32, %runtime._defer* }, align 4
|
||||
%deferPtr = alloca %runtime._defer*, align 4
|
||||
store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%0 = call i8* @llvm.stacksave()
|
||||
call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
|
||||
%defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
|
||||
store i32 0, i32* %defer.alloca.repack, align 4
|
||||
%defer.alloca.repack16 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
|
||||
store %runtime._defer* null, %runtime._defer** %defer.alloca.repack16, align 4
|
||||
%1 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
||||
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %1, 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
br i1 %setjmp.result, label %2, label %lpad
|
||||
|
||||
2: ; preds = %entry
|
||||
call void @main.external(i8* undef) #3
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.loophead: ; preds = %4, %2
|
||||
%3 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||
%stackIsNil = icmp eq %runtime._defer* %3, null
|
||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||
|
||||
rundefers.loop: ; preds = %rundefers.loophead
|
||||
%stack.next.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %3, i32 0, i32 1
|
||||
%stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
|
||||
store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
|
||||
%callback.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %3, i32 0, i32 0
|
||||
%callback = load i32, i32* %callback.gep, align 4
|
||||
switch i32 %callback, label %rundefers.default [
|
||||
i32 0, label %rundefers.callback0
|
||||
]
|
||||
|
||||
rundefers.callback0: ; preds = %rundefers.loop
|
||||
%setjmp1 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result2 = icmp eq i32 %setjmp1, 0
|
||||
br i1 %setjmp.result2, label %4, label %lpad
|
||||
|
||||
4: ; preds = %rundefers.callback0
|
||||
call void @"main.deferSimple$1"(i8* undef)
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.default: ; preds = %rundefers.loop
|
||||
unreachable
|
||||
|
||||
rundefers.end: ; preds = %rundefers.loophead
|
||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||
ret void
|
||||
|
||||
recover: ; preds = %rundefers.end3
|
||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||
ret void
|
||||
|
||||
lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry
|
||||
br label %rundefers.loophead6
|
||||
|
||||
rundefers.loophead6: ; preds = %6, %lpad
|
||||
%5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||
%stackIsNil7 = icmp eq %runtime._defer* %5, null
|
||||
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
|
||||
|
||||
rundefers.loop5: ; preds = %rundefers.loophead6
|
||||
%stack.next.gep8 = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 1
|
||||
%stack.next9 = load %runtime._defer*, %runtime._defer** %stack.next.gep8, align 4
|
||||
store %runtime._defer* %stack.next9, %runtime._defer** %deferPtr, align 4
|
||||
%callback.gep10 = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 0
|
||||
%callback11 = load i32, i32* %callback.gep10, align 4
|
||||
switch i32 %callback11, label %rundefers.default4 [
|
||||
i32 0, label %rundefers.callback012
|
||||
]
|
||||
|
||||
rundefers.callback012: ; preds = %rundefers.loop5
|
||||
%setjmp14 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result15 = icmp eq i32 %setjmp14, 0
|
||||
br i1 %setjmp.result15, label %6, label %lpad
|
||||
|
||||
6: ; preds = %rundefers.callback012
|
||||
call void @"main.deferSimple$1"(i8* undef)
|
||||
br label %rundefers.loophead6
|
||||
|
||||
rundefers.default4: ; preds = %rundefers.loop5
|
||||
unreachable
|
||||
|
||||
rundefers.end3: ; preds = %rundefers.loophead6
|
||||
br label %recover
|
||||
}
|
||||
|
||||
; Function Attrs: nofree nosync nounwind willreturn
|
||||
declare i8* @llvm.stacksave() #2
|
||||
|
||||
declare void @runtime.setupDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.deferSimple$1"(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printint32(i32 3, i8* undef) #3
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.destroyDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*) #0
|
||||
|
||||
declare void @runtime.printint32(i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferMultiple(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%defer.alloca2 = alloca { i32, %runtime._defer* }, align 4
|
||||
%defer.alloca = alloca { i32, %runtime._defer* }, align 4
|
||||
%deferPtr = alloca %runtime._defer*, align 4
|
||||
store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%0 = call i8* @llvm.stacksave()
|
||||
call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
|
||||
%defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
|
||||
store i32 0, i32* %defer.alloca.repack, align 4
|
||||
%defer.alloca.repack26 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
|
||||
store %runtime._defer* null, %runtime._defer** %defer.alloca.repack26, align 4
|
||||
%1 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
||||
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %1, align 4
|
||||
%defer.alloca2.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca2, i32 0, i32 0
|
||||
store i32 1, i32* %defer.alloca2.repack, align 4
|
||||
%defer.alloca2.repack27 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca2, i32 0, i32 1
|
||||
%2 = bitcast %runtime._defer** %defer.alloca2.repack27 to { i32, %runtime._defer* }**
|
||||
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %2, align 4
|
||||
%3 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
||||
store { i32, %runtime._defer* }* %defer.alloca2, { i32, %runtime._defer* }** %3, 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
br i1 %setjmp.result, label %4, label %lpad
|
||||
|
||||
4: ; preds = %entry
|
||||
call void @main.external(i8* undef) #3
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.loophead: ; preds = %7, %6, %4
|
||||
%5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||
%stackIsNil = icmp eq %runtime._defer* %5, null
|
||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||
|
||||
rundefers.loop: ; preds = %rundefers.loophead
|
||||
%stack.next.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 1
|
||||
%stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
|
||||
store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
|
||||
%callback.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 0
|
||||
%callback = load i32, i32* %callback.gep, align 4
|
||||
switch i32 %callback, label %rundefers.default [
|
||||
i32 0, label %rundefers.callback0
|
||||
i32 1, label %rundefers.callback1
|
||||
]
|
||||
|
||||
rundefers.callback0: ; preds = %rundefers.loop
|
||||
%setjmp4 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result5 = icmp eq i32 %setjmp4, 0
|
||||
br i1 %setjmp.result5, label %6, label %lpad
|
||||
|
||||
6: ; preds = %rundefers.callback0
|
||||
call void @"main.deferMultiple$1"(i8* undef)
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.callback1: ; preds = %rundefers.loop
|
||||
%setjmp7 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result8 = icmp eq i32 %setjmp7, 0
|
||||
br i1 %setjmp.result8, label %7, label %lpad
|
||||
|
||||
7: ; preds = %rundefers.callback1
|
||||
call void @"main.deferMultiple$2"(i8* undef)
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.default: ; preds = %rundefers.loop
|
||||
unreachable
|
||||
|
||||
rundefers.end: ; preds = %rundefers.loophead
|
||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||
ret void
|
||||
|
||||
recover: ; preds = %rundefers.end9
|
||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||
ret void
|
||||
|
||||
lpad: ; preds = %rundefers.callback122, %rundefers.callback018, %rundefers.callback1, %rundefers.callback0, %entry
|
||||
br label %rundefers.loophead12
|
||||
|
||||
rundefers.loophead12: ; preds = %10, %9, %lpad
|
||||
%8 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||
%stackIsNil13 = icmp eq %runtime._defer* %8, null
|
||||
br i1 %stackIsNil13, label %rundefers.end9, label %rundefers.loop11
|
||||
|
||||
rundefers.loop11: ; preds = %rundefers.loophead12
|
||||
%stack.next.gep14 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 1
|
||||
%stack.next15 = load %runtime._defer*, %runtime._defer** %stack.next.gep14, align 4
|
||||
store %runtime._defer* %stack.next15, %runtime._defer** %deferPtr, align 4
|
||||
%callback.gep16 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 0
|
||||
%callback17 = load i32, i32* %callback.gep16, align 4
|
||||
switch i32 %callback17, label %rundefers.default10 [
|
||||
i32 0, label %rundefers.callback018
|
||||
i32 1, label %rundefers.callback122
|
||||
]
|
||||
|
||||
rundefers.callback018: ; preds = %rundefers.loop11
|
||||
%setjmp20 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result21 = icmp eq i32 %setjmp20, 0
|
||||
br i1 %setjmp.result21, label %9, label %lpad
|
||||
|
||||
9: ; preds = %rundefers.callback018
|
||||
call void @"main.deferMultiple$1"(i8* undef)
|
||||
br label %rundefers.loophead12
|
||||
|
||||
rundefers.callback122: ; preds = %rundefers.loop11
|
||||
%setjmp24 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||
%setjmp.result25 = icmp eq i32 %setjmp24, 0
|
||||
br i1 %setjmp.result25, label %10, label %lpad
|
||||
|
||||
10: ; preds = %rundefers.callback122
|
||||
call void @"main.deferMultiple$2"(i8* undef)
|
||||
br label %rundefers.loophead12
|
||||
|
||||
rundefers.default10: ; preds = %rundefers.loop11
|
||||
unreachable
|
||||
|
||||
rundefers.end9: ; preds = %rundefers.loophead12
|
||||
br label %recover
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.deferMultiple$1"(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printint32(i32 3, i8* undef) #3
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.deferMultiple$2"(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printint32(i32 5, i8* undef) #3
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+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,+strict-align,+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 = { nofree nosync nounwind willreturn }
|
||||
attributes #3 = { nounwind }
|
||||
attributes #4 = { nounwind returns_twice }
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
func external()
|
||||
|
||||
func deferSimple() {
|
||||
defer func() {
|
||||
print(3)
|
||||
}()
|
||||
external()
|
||||
}
|
||||
|
||||
func deferMultiple() {
|
||||
defer func() {
|
||||
print(3)
|
||||
}()
|
||||
defer func() {
|
||||
print(5)
|
||||
}()
|
||||
external()
|
||||
}
|
||||
Vendored
+13
-12
@@ -3,18 +3,18 @@ source_filename = "float.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 triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -26,25 +26,25 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.maxu32f(i8* %context) unnamed_addr #0 {
|
||||
define hidden float @main.maxu32f(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float 0x41F0000000000000
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 -1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context) unnamed_addr #0 {
|
||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %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, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = uitofp i32 %v to float
|
||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||
@@ -54,7 +54,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #0 {
|
||||
define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -67,7 +67,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||
@@ -79,7 +79,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||
@@ -92,4 +92,5 @@ entry:
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
|
||||
Vendored
+15
-13
@@ -3,45 +3,47 @@ source_filename = "func.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 triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq void ()* %callback.funcptr, null
|
||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
fpcall.next: ; preds = %entry
|
||||
%1 = bitcast void ()* %callback.funcptr to void (i32, i8*)*
|
||||
call void %1(i32 3, i8* %callback.context) #0
|
||||
call void %1(i32 3, i8* %callback.context) #2
|
||||
ret void
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.nilPanic(i8*)
|
||||
declare void @runtime.nilPanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.bar(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.bar(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @main.foo(i8* undef, void ()* bitcast (void (i32, i8*)* @main.someFunc to void ()*), i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.someFunc(i32 %arg0, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.someFunc(i32 %arg0, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+45
-43
@@ -26,91 +26,91 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:complex128", i32 0 }
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:complex128", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newScalar(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.newScalar(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
||||
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||
store i8* %new, i8** @main.scalar1, align 4
|
||||
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
|
||||
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
||||
store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4
|
||||
%new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
|
||||
%new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
||||
store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4
|
||||
%new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
|
||||
%new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
|
||||
store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newArray(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.newArray(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
||||
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||
store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4
|
||||
%new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
|
||||
%new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
||||
store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4
|
||||
%new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
|
||||
%new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
||||
store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newStruct(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.newStruct(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
||||
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||
store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4
|
||||
%new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
|
||||
%new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
||||
store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4
|
||||
%new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
|
||||
%new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
||||
store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
|
||||
%new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
|
||||
%new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
|
||||
store i8* %new3, i8** bitcast ({ i8*, [61 x i32] }** @main.struct4 to i8**), align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #0 {
|
||||
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #0
|
||||
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #2
|
||||
%0 = bitcast i8* %new to { i8*, void ()* }*
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||
ret { i8*, void ()* }* %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeSlice(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.makeSlice(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #0
|
||||
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
|
||||
store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8
|
||||
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 1), align 4
|
||||
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 2), align 8
|
||||
%makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #0
|
||||
%makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #2
|
||||
store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8
|
||||
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 1), align 4
|
||||
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 2), align 8
|
||||
%makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #0
|
||||
%makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #2
|
||||
store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8
|
||||
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 1), align 4
|
||||
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 2), align 8
|
||||
@@ -118,18 +118,20 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||
%.repack = bitcast i8* %0 to double*
|
||||
store double %v.r, double* %.repack, align 8
|
||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%1 = bitcast i8* %.repack1 to double*
|
||||
store double %v.i, double* %1, align 8
|
||||
%2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||
ret %runtime._interface %2
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+50
-48
@@ -3,67 +3,67 @@ source_filename = "go1.17.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 triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = getelementptr i8, i8* %p, i32 %len
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = trunc i64 %len to i32
|
||||
%1 = getelementptr i8, i8* %p, i32 %0
|
||||
call void @runtime.trackPointer(i8* %1, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %1, i8* undef) #2
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp ult i32 %s.len, 4
|
||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
call void @runtime.sliceToArrayPointerPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
slicetoarray.next: ; preds = %entry
|
||||
%1 = bitcast i32* %s.data to [4 x i32]*
|
||||
ret [4 x i32]* %1
|
||||
}
|
||||
|
||||
declare void @runtime.sliceToArrayPointerPanic(i8*)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #0
|
||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
call void @runtime.sliceToArrayPointerPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.sliceToArrayPointerPanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
|
||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.next: ; preds = %entry
|
||||
%0 = bitcast i8* %makeslice to [4 x i32]*
|
||||
ret [4 x i32]* %0
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp ugt i32 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
@@ -72,44 +72,44 @@ entry:
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
|
||||
%8 = bitcast i32* %ptr to i8*
|
||||
call void @runtime.trackPointer(i8* %8, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %8, i8* undef) #2
|
||||
ret { i32*, i32, i32 } %7
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.unsafeSlicePanic(i8*)
|
||||
declare void @runtime.unsafeSlicePanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i8* %ptr, null
|
||||
%1 = icmp ne i16 %len, 0
|
||||
%2 = and i1 %0, %1
|
||||
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%3 = zext i16 %len to i32
|
||||
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
|
||||
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
|
||||
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
|
||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
||||
ret { i8*, i32, i32 } %6
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
@@ -118,22 +118,22 @@ entry:
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = trunc i64 %len to i32
|
||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||
%9 = bitcast i32* %ptr to i8*
|
||||
call void @runtime.trackPointer(i8* %9, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %9, i8* undef) #2
|
||||
ret { i32*, i32, i32 } %8
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
@@ -142,18 +142,20 @@ entry:
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = trunc i64 %len to i32
|
||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||
%9 = bitcast i32* %ptr to i8*
|
||||
call void @runtime.trackPointer(i8* %9, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %9, i8* undef) #2
|
||||
ret { i32*, i32, i32 } %8
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
+54
-52
@@ -5,59 +5,59 @@ target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
||||
%"internal/task.gcData" = type {}
|
||||
%"internal/task.state" = type { i32, i32* }
|
||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #0
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef) #8
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, i8*)
|
||||
declare void @main.regularFunction(i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint i8* %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, i8* undef) #0
|
||||
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*)
|
||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*) #0
|
||||
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*)
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #0
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint i8* %0 to i32
|
||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
|
||||
@@ -65,26 +65,26 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
|
||||
%0 = bitcast i8* %n to i32*
|
||||
store i32 3, i32* %0, align 4
|
||||
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #0
|
||||
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
|
||||
%2 = bitcast i8* %1 to i32*
|
||||
store i32 5, i32* %2, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
store i8* %n, i8** %4, align 4
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef) #0
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef) #8
|
||||
%5 = load i32, i32* %0, align 4
|
||||
call void @runtime.printint32(i32 %5, i8* undef) #0
|
||||
call void @runtime.printint32(i32 %5, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%unpack.ptr = bitcast i8* %context to i32*
|
||||
store i32 7, i32* %unpack.ptr, align 4
|
||||
@@ -92,7 +92,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
%2 = load i32, i32* %1, align 4
|
||||
@@ -103,12 +103,12 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.printint32(i32, i8*)
|
||||
declare void @runtime.printint32(i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
|
||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
store i32 5, i32* %1, align 4
|
||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
@@ -117,13 +117,13 @@ entry:
|
||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%5 = bitcast i8* %4 to void ()**
|
||||
store void ()* %fn.funcptr, void ()** %5, align 4
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #0
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef) #8
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #4 {
|
||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
%2 = load i32, i32* %1, align 4
|
||||
@@ -133,38 +133,38 @@ entry:
|
||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%7 = bitcast i8* %6 to void (i32, i8*)**
|
||||
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
||||
call void %8(i32 %2, i8* %5) #0
|
||||
call void %8(i32 %2, i8* %5) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #0
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
|
||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
|
||||
%1 = bitcast i8* %0 to i8**
|
||||
store i8* %itf.value, i8** %1, align 4
|
||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
@@ -176,15 +176,15 @@ entry:
|
||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||
%5 = bitcast i8* %4 to i32*
|
||||
store i32 %itf.typecode, i32* %5, align 4
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #0
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* undef) #8
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
|
||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
|
||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i8**
|
||||
%2 = load i8*, i8** %1, align 4
|
||||
@@ -197,14 +197,16 @@ entry:
|
||||
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
||||
%10 = bitcast i8* %9 to i32*
|
||||
%11 = load i32, i32* %10, align 4
|
||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #0
|
||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #4 = { nounwind "tinygo-gowrapper" }
|
||||
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+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,+strict-align,+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,+strict-align,+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,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||
attributes #6 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
attributes #7 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||
attributes #8 = { nounwind }
|
||||
|
||||
+61
-59
@@ -5,7 +5,7 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
||||
%"internal/task.gcData" = type { i8* }
|
||||
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
||||
%"internal/task.stackState" = type { i32, i32 }
|
||||
@@ -13,84 +13,84 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, i8*)
|
||||
declare void @main.regularFunction(i32, i8*) #0
|
||||
|
||||
declare void @runtime.deadlock(i8*)
|
||||
declare void @runtime.deadlock(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint i8* %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, i8* undef) #0
|
||||
call void @runtime.deadlock(i8* undef) #0
|
||||
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
|
||||
call void @runtime.deadlock(i8* undef) #8
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*)
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint i8* %0 to i32
|
||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
|
||||
call void @runtime.deadlock(i8* undef) #0
|
||||
call void @runtime.deadlock(i8* undef) #8
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
|
||||
%0 = bitcast i8* %n to i32*
|
||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
|
||||
store i32 3, i32* %0, align 4
|
||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #0
|
||||
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
|
||||
call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #8
|
||||
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
|
||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #8
|
||||
%2 = bitcast i8* %1 to i32*
|
||||
store i32 5, i32* %2, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
store i8* %n, i8** %4, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #8
|
||||
%5 = load i32, i32* %0, align 4
|
||||
call void @runtime.printint32(i32 %5, i8* undef) #0
|
||||
call void @runtime.printint32(i32 %5, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%unpack.ptr = bitcast i8* %context to i32*
|
||||
store i32 7, i32* %unpack.ptr, align 4
|
||||
@@ -98,7 +98,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
%2 = load i32, i32* %1, align 4
|
||||
@@ -106,17 +106,17 @@ entry:
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
%5 = load i8*, i8** %4, align 4
|
||||
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
|
||||
call void @runtime.deadlock(i8* undef) #0
|
||||
call void @runtime.deadlock(i8* undef) #8
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.printint32(i32, i8*)
|
||||
declare void @runtime.printint32(i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
store i32 5, i32* %1, align 4
|
||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
@@ -125,12 +125,12 @@ entry:
|
||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%5 = bitcast i8* %4 to void ()**
|
||||
store void ()* %fn.funcptr, void ()** %5, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 16384, i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 16384, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #4 {
|
||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i32*
|
||||
%2 = load i32, i32* %1, align 4
|
||||
@@ -140,40 +140,40 @@ entry:
|
||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%7 = bitcast i8* %6 to void (i32, i8*)**
|
||||
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
||||
call void %8(i32 %2, i8* %5) #0
|
||||
call void @runtime.deadlock(i8* undef) #0
|
||||
call void %8(i32 %2, i8* %5) #8
|
||||
call void @runtime.deadlock(i8* undef) #8
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #0
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
|
||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
|
||||
%1 = bitcast i8* %0 to i8**
|
||||
store i8* %itf.value, i8** %1, align 4
|
||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
@@ -185,14 +185,14 @@ entry:
|
||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||
%5 = bitcast i8* %4 to i32*
|
||||
store i32 %itf.typecode, i32* %5, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 16384, i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 16384, i8* undef) #8
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
|
||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
|
||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i8**
|
||||
%2 = load i8*, i8** %1, align 4
|
||||
@@ -205,15 +205,17 @@ entry:
|
||||
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
||||
%10 = bitcast i8* %9 to i32*
|
||||
%11 = load i32, i32* %10, align 4
|
||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #0
|
||||
call void @runtime.deadlock(i8* undef) #0
|
||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #8
|
||||
call void @runtime.deadlock(i8* undef) #8
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #4 = { nounwind "tinygo-gowrapper" }
|
||||
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #4 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
|
||||
attributes #6 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
attributes #7 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||
attributes #8 = { nounwind }
|
||||
|
||||
Vendored
+51
-49
@@ -22,111 +22,113 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"]
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
||||
}
|
||||
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #1
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
||||
}
|
||||
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #2
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #0
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #6
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
ret i1 %typecode
|
||||
}
|
||||
|
||||
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef) #0
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
ret i1 %0
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef) #6
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #3
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #0
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #6
|
||||
%1 = extractvalue %runtime._string %0, 0
|
||||
call void @runtime.trackPointer(i8* %1, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %1, i8* undef) #6
|
||||
ret %runtime._string %0
|
||||
}
|
||||
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #4
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #5
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #6 = { nounwind }
|
||||
|
||||
+11
-9
@@ -3,30 +3,32 @@ source_filename = "intrinsics.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call double @math.Sqrt(double %x, i8* undef) #0
|
||||
%0 = call double @math.Sqrt(double %x, i8* undef) #2
|
||||
ret double %0
|
||||
}
|
||||
|
||||
declare double @math.Sqrt(double, i8*)
|
||||
declare double @math.Sqrt(double, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call double @math.Trunc(double %x, i8* undef) #0
|
||||
%0 = call double @math.Trunc(double %x, i8* undef) #2
|
||||
ret double %0
|
||||
}
|
||||
|
||||
declare double @math.Trunc(double, i8*)
|
||||
declare double @math.Trunc(double, i8*) #0
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+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,+strict-align,+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 }
|
||||
|
||||
Vendored
+10
-9
@@ -3,35 +3,36 @@ source_filename = "intrinsics.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 triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call double @llvm.sqrt.f64(double %x)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||
declare double @llvm.sqrt.f64(double) #1
|
||||
declare double @llvm.sqrt.f64(double) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call double @llvm.trunc.f64(double %x)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||
declare double @llvm.trunc.f64(double) #1
|
||||
declare double @llvm.trunc.f64(double) #2
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nofree nosync nounwind readnone speculatable willreturn }
|
||||
|
||||
Vendored
+25
-23
@@ -3,76 +3,78 @@ source_filename = "pointer.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 triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret [0 x i32] zeroinitializer
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = bitcast i8* %x to i32*
|
||||
call void @runtime.trackPointer(i8* %x, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %x, i8* undef) #2
|
||||
ret i32* %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = bitcast i32* %x to i8*
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.trackPointer(i8* %x, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %x, i8* undef) #2
|
||||
ret i8* %x
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = bitcast i32* %ptr to i8*
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||
%1 = getelementptr i32, i32* %ptr, i32 %offset
|
||||
%2 = bitcast i32* %1 to i8*
|
||||
call void @runtime.trackPointer(i8* %2, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %2, i8* undef) #2
|
||||
%3 = bitcast i32* %1 to i8*
|
||||
call void @runtime.trackPointer(i8* %3, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %3, i8* undef) #2
|
||||
ret i32* %1
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+17
-16
@@ -10,58 +10,59 @@ target triple = "wasm32-unknown-wasi"
|
||||
@undefinedGlobalNotInSection = external global i32, align 4
|
||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @extern_func() #1 {
|
||||
define void @extern_func() #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @somepkg.someFunction2(i8*)
|
||||
declare void @somepkg.someFunction2(i8*) #0
|
||||
|
||||
; Function Attrs: inlinehint nounwind
|
||||
define hidden void @main.inlineFunc(i8* %context) unnamed_addr #2 {
|
||||
define hidden void @main.inlineFunc(i8* %context) unnamed_addr #3 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #3 {
|
||||
define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #4 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.functionInSection(i8* %context) unnamed_addr #0 section ".special_function_section" {
|
||||
define hidden void @main.functionInSection(i8* %context) unnamed_addr #1 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @exportedFunctionInSection() #4 section ".special_function_section" {
|
||||
define void @exportedFunctionInSection() #5 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.undefinedFunctionNotInSection(i8*)
|
||||
declare void @main.undefinedFunctionNotInSection(i8*) #0
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { nounwind "wasm-export-name"="extern_func" }
|
||||
attributes #2 = { inlinehint nounwind }
|
||||
attributes #3 = { noinline nounwind }
|
||||
attributes #4 = { nounwind "wasm-export-name"="exportedFunctionInSection" }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
|
||||
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
|
||||
|
||||
Vendored
+55
-53
@@ -3,51 +3,51 @@ source_filename = "slice.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 triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %ints.len
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %ints.cap
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %ints.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
lookup.next: ; preds = %entry
|
||||
%0 = getelementptr inbounds i32, i32* %ints.data, i32 %index
|
||||
%1 = load i32, i32* %0, align 4
|
||||
ret i32 %1
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(i8*)
|
||||
declare void @runtime.lookupPanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #0
|
||||
%varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #2
|
||||
%0 = bitcast i8* %varargs to i32*
|
||||
store i32 1, i32* %0, align 4
|
||||
%1 = getelementptr inbounds i8, i8* %varargs, i32 4
|
||||
@@ -57,7 +57,7 @@ entry:
|
||||
%4 = bitcast i8* %3 to i32*
|
||||
store i32 3, i32* %4, align 4
|
||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, i8* undef) #0
|
||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, i8* undef) #2
|
||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||
@@ -65,18 +65,18 @@ entry:
|
||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %append.newLen, 1
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %append.newCap, 2
|
||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #2
|
||||
ret { i32*, i32, i32 } %7
|
||||
}
|
||||
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*)
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||
%append.srcPtr1 = bitcast i32* %added.data to i8*
|
||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* %append.srcPtr1, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, i8* undef) #0
|
||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* %append.srcPtr1, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, i8* undef) #2
|
||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||
@@ -84,103 +84,105 @@ entry:
|
||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %append.newLen, 1
|
||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %append.newCap, 2
|
||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #2
|
||||
ret { i32*, i32, i32 } %2
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceCopy(i32* %dst.data, i32 %dst.len, i32 %dst.cap, i32* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.sliceCopy(i32* %dst.data, i32 %dst.len, i32 %dst.cap, i32* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%copy.dstPtr = bitcast i32* %dst.data to i8*
|
||||
%copy.srcPtr = bitcast i32* %src.data to i8*
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef) #0
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef) #2
|
||||
ret i32 %copy.n
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
||||
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||
ret { i8*, i32, i32 } %2
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.slicePanic(i8*)
|
||||
declare void @runtime.slicePanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = shl i32 %len, 1
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
||||
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
||||
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||
ret { i16*, i32, i32 } %2
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = mul i32 %len, 3
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
||||
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
|
||||
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||
ret { [3 x i8]*, i32, i32 } %2
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context) unnamed_addr #0 {
|
||||
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = shl i32 %len, 2
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||
ret { i32*, i32, i32 } %2
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+28
-26
@@ -7,93 +7,95 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.someString(i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._string @main.someString(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main$string", i32 0, i32 0), i32 3 }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.zeroLengthString(i8* %context) unnamed_addr #0 {
|
||||
define hidden %runtime._string @main.zeroLengthString(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret %runtime._string zeroinitializer
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context) unnamed_addr #0 {
|
||||
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %s.len
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %s.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
lookup.next: ; preds = %entry
|
||||
%0 = getelementptr inbounds i8, i8* %s.data, i32 %index
|
||||
%1 = load i8, i8* %0, align 1
|
||||
ret i8 %1
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(i8*)
|
||||
declare void @runtime.lookupPanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #0
|
||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #2
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*)
|
||||
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #0
|
||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #2
|
||||
%1 = xor i1 %0, true
|
||||
ret i1 %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
|
||||
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef) #0
|
||||
%0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef) #2
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*)
|
||||
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context) unnamed_addr #0 {
|
||||
define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = zext i8 %x to i32
|
||||
%.not = icmp ult i32 %0, %s.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef) #0
|
||||
unreachable
|
||||
|
||||
lookup.next: ; preds = %entry
|
||||
%1 = getelementptr inbounds i8, i8* %s.data, i32 %0
|
||||
%2 = load i8, i8* %1, align 1
|
||||
ret i8 %2
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
+11
-14
@@ -3,28 +3,25 @@ package compiler
|
||||
// This file implements volatile loads/stores in runtime/volatile.LoadT and
|
||||
// runtime/volatile.StoreT as compiler builtins.
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createVolatileLoad is the implementation of the intrinsic function
|
||||
// runtime/volatile.LoadT().
|
||||
func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
addr := b.getValue(instr.Args[0])
|
||||
b.createNilCheck(instr.Args[0], addr, "deref")
|
||||
func (b *builder) createVolatileLoad() {
|
||||
b.createFunctionStart()
|
||||
addr := b.getValue(b.fn.Params[0])
|
||||
b.createNilCheck(b.fn.Params[0], addr, "deref")
|
||||
val := b.CreateLoad(addr, "")
|
||||
val.SetVolatile(true)
|
||||
return val, nil
|
||||
b.CreateRet(val)
|
||||
}
|
||||
|
||||
// createVolatileStore is the implementation of the intrinsic function
|
||||
// runtime/volatile.StoreT().
|
||||
func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
addr := b.getValue(instr.Args[0])
|
||||
val := b.getValue(instr.Args[1])
|
||||
b.createNilCheck(instr.Args[0], addr, "deref")
|
||||
func (b *builder) createVolatileStore() {
|
||||
b.createFunctionStart()
|
||||
addr := b.getValue(b.fn.Params[0])
|
||||
val := b.getValue(b.fn.Params[1])
|
||||
b.createNilCheck(b.fn.Params[0], addr, "deref")
|
||||
store := b.CreateStore(val, addr)
|
||||
store.SetVolatile(true)
|
||||
return llvm.Value{}, nil
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee
|
||||
github.com/chromedp/chromedp v0.7.6
|
||||
@@ -13,7 +13,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
go.bug.st/serial v1.1.3
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||
golang.org/x/tools v0.1.11
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f
|
||||
tinygo.org/x/go-llvm v0.0.0-20220626113704-45f1e2dbf887
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a h1:QPU7APo6y/6VkCDq6HU3WWIUzER8iywSac23+1UQv60=
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc h1:Yp49g+qqgQRPk/gcRSmAsXgnT16XPJ6Y5JM1poc6gYM=
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/chromedp/cdproto v0.0.0-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
||||
@@ -40,45 +40,44 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE=
|
||||
go.bug.st/serial v1.1.3/go.mod h1:8TT7u/SwwNIpJ8QaG4s+HTjFt9ReXs2cdOU7ZEk50Dk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f h1:9/J+NpFTpAhYcbh1mC4dr9W/aAPnwrUh8dmq21xWdSM=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220626113704-45f1e2dbf887 h1:k+Y1DU/WoBDkTkRJGF149yk3S2K2VhNglN435DXDS5s=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220626113704-45f1e2dbf887/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.23.0"
|
||||
const Version = "0.24.0"
|
||||
|
||||
var (
|
||||
// This variable is set at build time using -ldflags parameters.
|
||||
|
||||
+4
-1
@@ -18,6 +18,7 @@ var (
|
||||
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||
)
|
||||
|
||||
// This is one of the errors that can be returned from toLLVMValue when the
|
||||
@@ -26,7 +27,9 @@ var (
|
||||
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
||||
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||
err == errLoopUnrolled
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
|
||||
+18
-3
@@ -50,10 +50,17 @@ func newRunner(mod llvm.Module, debug bool) *runner {
|
||||
return &r
|
||||
}
|
||||
|
||||
// Dispose deallocates all alloated LLVM resources.
|
||||
func (r *runner) dispose() {
|
||||
r.targetData.Dispose()
|
||||
r.targetData = llvm.TargetData{}
|
||||
}
|
||||
|
||||
// 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, debug bool) error {
|
||||
r := newRunner(mod, debug)
|
||||
defer r.dispose()
|
||||
|
||||
initAll := mod.NamedFunction("runtime.initAll")
|
||||
bb := initAll.EntryBasicBlock()
|
||||
@@ -121,7 +128,10 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
|
||||
// Make sure that any globals touched by the package
|
||||
// initializer, won't be accessed by later package initializers.
|
||||
r.markExternalLoad(fn)
|
||||
err := r.markExternalLoad(fn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to interpret package %s: %w", r.pkgName, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
return callErr
|
||||
@@ -193,6 +203,7 @@ func RunFunc(fn llvm.Value, debug bool) error {
|
||||
// Create and initialize *runner object.
|
||||
mod := fn.GlobalParent()
|
||||
r := newRunner(mod, debug)
|
||||
defer r.dispose()
|
||||
initName := fn.Name()
|
||||
if !strings.HasSuffix(initName, ".init") {
|
||||
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
||||
@@ -288,12 +299,16 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||
// variable. Another package initializer might read from the same global
|
||||
// variable. By marking this function as being run at runtime, that load
|
||||
// instruction will need to be run at runtime instead of at compile time.
|
||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) error {
|
||||
mem := memoryView{r: r}
|
||||
mem.markExternalLoad(llvmValue)
|
||||
err := mem.markExternalLoad(llvmValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for index, obj := range mem.objects {
|
||||
if obj.marked > r.objects[index].marked {
|
||||
r.objects[index].marked = obj.marked
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+13
-1
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -10,6 +11,12 @@ import (
|
||||
)
|
||||
|
||||
func TestInterp(t *testing.T) {
|
||||
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||
if err != nil {
|
||||
// Note: this should never happen and if it does, it will always happen
|
||||
// for a particular build because llvm.Version is a constant.
|
||||
panic(err)
|
||||
}
|
||||
for _, name := range []string{
|
||||
"basic",
|
||||
"phi",
|
||||
@@ -19,7 +26,10 @@ func TestInterp(t *testing.T) {
|
||||
"revert",
|
||||
"alloc",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
name := name // make local to this closure
|
||||
if name == "slice-copy" && llvmVersion < 14 {
|
||||
continue
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(t, "testdata/"+name)
|
||||
@@ -30,6 +40,7 @@ func TestInterp(t *testing.T) {
|
||||
func runTest(t *testing.T, pathPrefix string) {
|
||||
// Read the input IR.
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
|
||||
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
|
||||
if err != nil {
|
||||
@@ -39,6 +50,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
||||
if err != nil {
|
||||
t.Fatalf("could not load module:\n%v", err)
|
||||
}
|
||||
defer mod.Dispose()
|
||||
|
||||
// Perform the transform.
|
||||
err = Run(mod, false)
|
||||
|
||||
+56
-4
@@ -24,15 +24,39 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
locals[i] = param
|
||||
}
|
||||
|
||||
// Track what blocks have run instructions at runtime.
|
||||
// This is used to prevent unrolling.
|
||||
var runtimeBlocks map[int]struct{}
|
||||
|
||||
// Start with the first basic block and the first instruction.
|
||||
// Branch instructions may modify both bb and instIndex when branching.
|
||||
bb := fn.blocks[0]
|
||||
currentBB := 0
|
||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||
var operands []value
|
||||
startRTInsts := len(mem.instructions)
|
||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||
if instIndex == 0 {
|
||||
// This is the start of a new basic block.
|
||||
if len(mem.instructions) != startRTInsts {
|
||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||
// This loop has been unrolled.
|
||||
// Avoid doing this, as it can result in a large amount of extra machine code.
|
||||
// This currently uses the branch from the last block, as there is no available information to give a better location.
|
||||
lastBBInsts := fn.blocks[lastBB].instructions
|
||||
return nil, mem, r.errorAt(lastBBInsts[len(lastBBInsts)-1], errLoopUnrolled)
|
||||
}
|
||||
|
||||
// Flag the last block as having run stuff at runtime.
|
||||
if runtimeBlocks == nil {
|
||||
runtimeBlocks = make(map[int]struct{})
|
||||
}
|
||||
runtimeBlocks[lastBB] = struct{}{}
|
||||
|
||||
// Reset the block-start runtime instructions counter.
|
||||
startRTInsts = len(mem.instructions)
|
||||
}
|
||||
|
||||
// There may be PHI nodes that need to be resolved. Resolve all PHI
|
||||
// nodes before continuing with regular instructions.
|
||||
// PHI nodes need to be treated specially because they can have a
|
||||
@@ -81,12 +105,28 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
if inst.opcode != llvm.PHI {
|
||||
for _, v := range inst.operands {
|
||||
if v, ok := v.(localValue); ok {
|
||||
if localVal := locals[fn.locals[v.value]]; localVal == nil {
|
||||
index, ok := fn.locals[v.value]
|
||||
if !ok {
|
||||
// This is a localValue that is not local to the
|
||||
// function. An example would be an inline assembly call
|
||||
// operand.
|
||||
isRuntimeInst = true
|
||||
break
|
||||
}
|
||||
localVal := locals[index]
|
||||
if localVal == nil {
|
||||
// Trying to read a function-local value before it is
|
||||
// set.
|
||||
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
|
||||
} else {
|
||||
operands = append(operands, localVal)
|
||||
if _, ok := localVal.(localValue); ok {
|
||||
// The function-local value is still just a
|
||||
// localValue (which can't be interpreted at compile
|
||||
// time). Not sure whether this ever happens in
|
||||
// practice.
|
||||
isRuntimeInst = true
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -219,6 +259,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
case callFn.name == "internal/task.Pause":
|
||||
// Task scheduling isn't possible at compile time.
|
||||
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
|
||||
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
|
||||
// The time package contains a call to runtime.nanotime.
|
||||
// This appears to be to work around a limitation in Windows
|
||||
@@ -926,12 +969,18 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
args := operands[:len(operands)-1]
|
||||
for _, arg := range args {
|
||||
if arg.Type().TypeKind() == llvm.PointerTypeKind {
|
||||
mem.markExternalStore(arg)
|
||||
err := mem.markExternalStore(arg)
|
||||
if err != nil {
|
||||
return r.errorAt(inst, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
result = r.builder.CreateCall(llvmFn, args, inst.name)
|
||||
case llvm.Load:
|
||||
mem.markExternalLoad(operands[0])
|
||||
err := mem.markExternalLoad(operands[0])
|
||||
if err != nil {
|
||||
return r.errorAt(inst, err)
|
||||
}
|
||||
result = r.builder.CreateLoad(operands[0], inst.name)
|
||||
if inst.llvmInst.IsVolatile() {
|
||||
result.SetVolatile(true)
|
||||
@@ -940,7 +989,10 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
result.SetOrdering(ordering)
|
||||
}
|
||||
case llvm.Store:
|
||||
mem.markExternalStore(operands[1])
|
||||
err := mem.markExternalStore(operands[1])
|
||||
if err != nil {
|
||||
return r.errorAt(inst, err)
|
||||
}
|
||||
result = r.builder.CreateStore(operands[0], operands[1])
|
||||
if inst.llvmInst.IsVolatile() {
|
||||
result.SetVolatile(true)
|
||||
|
||||
+41
-14
@@ -17,6 +17,7 @@ package interp
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
@@ -104,28 +105,28 @@ func (mv *memoryView) revert() {
|
||||
// means that the interpreter can still read from it, but cannot write to it as
|
||||
// that would mean the external read (done at runtime) reads from a state that
|
||||
// would not exist had the whole initialization been done at runtime.
|
||||
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) {
|
||||
mv.markExternal(llvmValue, 1)
|
||||
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) error {
|
||||
return mv.markExternal(llvmValue, 1)
|
||||
}
|
||||
|
||||
// markExternalStore marks the given LLVM value as having an external write.
|
||||
// This means that the interpreter can no longer read from it or write to it, as
|
||||
// that would happen in a different order than if all initialization were
|
||||
// happening at runtime.
|
||||
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) {
|
||||
mv.markExternal(llvmValue, 2)
|
||||
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) error {
|
||||
return mv.markExternal(llvmValue, 2)
|
||||
}
|
||||
|
||||
// markExternal is a helper for markExternalLoad and markExternalStore, and
|
||||
// should not be called directly.
|
||||
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
||||
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||
if llvmValue.IsUndef() || llvmValue.IsNull() {
|
||||
// Null and undef definitely don't contain (valid) pointers.
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
|
||||
// These are considered external by default, there is nothing to mark.
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
if !llvmValue.IsAGlobalValue().IsNil() {
|
||||
@@ -144,7 +145,10 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
||||
// Using mark '2' (which means read/write access) because
|
||||
// even from an object that is only read from, the resulting
|
||||
// loaded pointer can be written to.
|
||||
mv.markExternal(initializer, 2)
|
||||
err := mv.markExternal(initializer, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is a function. Go through all instructions and mark all
|
||||
@@ -170,7 +174,10 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
||||
for i := 0; i < numOperands; i++ {
|
||||
// Using mark '2' (which means read/write access)
|
||||
// because this might be a store instruction.
|
||||
mv.markExternal(inst.Operand(i), 2)
|
||||
err := mv.markExternal(inst.Operand(i), 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,9 +186,22 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
||||
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
||||
switch llvmValue.Opcode() {
|
||||
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
|
||||
mv.markExternal(llvmValue.Operand(0), mark)
|
||||
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
|
||||
// Integer binary operators. Mark both operands.
|
||||
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mv.markExternal(llvmValue.Operand(1), mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
panic("interp: unknown constant expression")
|
||||
return fmt.Errorf("interp: unknown constant expression '%s'", instructionNameMap[llvmValue.Opcode()])
|
||||
}
|
||||
} else if !llvmValue.IsAInlineAsm().IsNil() {
|
||||
// Inline assembly can modify globals but only exported globals. Let's
|
||||
@@ -196,18 +216,25 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
||||
numElements := llvmType.StructElementTypesCount()
|
||||
for i := 0; i < numElements; i++ {
|
||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||
mv.markExternal(element, mark)
|
||||
err := mv.markExternal(element, mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case llvm.ArrayTypeKind:
|
||||
numElements := llvmType.ArrayLength()
|
||||
for i := 0; i < numElements; i++ {
|
||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||
mv.markExternal(element, mark)
|
||||
err := mv.markExternal(element, mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic("interp: unknown type kind in markExternalValue")
|
||||
return errors.New("interp: unknown type kind in markExternalValue")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasExternalLoadOrStore returns true if this object has an external load or
|
||||
|
||||
Vendored
+45
@@ -3,6 +3,8 @@ target triple = "x86_64--linux"
|
||||
|
||||
declare void @externalCall(i64)
|
||||
|
||||
declare i64 @ptrHash(i8* nocapture)
|
||||
|
||||
@foo.knownAtRuntime = global i64 0
|
||||
@bar.knownAtRuntime = global i64 0
|
||||
@baz.someGlobal = external global [3 x {i64, i32}]
|
||||
@@ -10,6 +12,8 @@ declare void @externalCall(i64)
|
||||
@x.atomicNum = global i32 0
|
||||
@x.volatileNum = global i32 0
|
||||
@y.ready = global i32 0
|
||||
@z.bloom = global i64 0
|
||||
@z.arr = global [32 x i8] zeroinitializer
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
@@ -19,6 +23,7 @@ entry:
|
||||
call void @main.init(i8* undef)
|
||||
call void @x.init(i8* undef)
|
||||
call void @y.init(i8* undef)
|
||||
call void @z.init(i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -72,3 +77,43 @@ loop:
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.init(i8* %context) unnamed_addr {
|
||||
%bloom = bitcast i64* @z.bloom to i8*
|
||||
|
||||
; This can be safely expanded.
|
||||
call void @z.setArr(i8* %bloom, i64 1, i8* %bloom)
|
||||
|
||||
; This call should be reverted to prevent unrolling.
|
||||
call void @z.setArr(i8* bitcast ([32 x i8]* @z.arr to i8*), i64 32, i8* %bloom)
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||
%idx = sub i64 %prev, 1
|
||||
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||
call void @z.set(i8* %elem, i8* %context)
|
||||
%done = icmp eq i64 %idx, 0
|
||||
br i1 %done, label %end, label %loop
|
||||
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||
; Insert the pointer into the Bloom filter.
|
||||
%hash = call i64 @ptrHash(i8* %ptr)
|
||||
%index = lshr i64 %hash, 58
|
||||
%bit = shl i64 1, %index
|
||||
%bloom = bitcast i8* %context to i64*
|
||||
%old = load i64, i64* %bloom
|
||||
%new = or i64 %old, %bit
|
||||
store i64 %new, i64* %bloom
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
+33
@@ -8,9 +8,13 @@ target triple = "x86_64--linux"
|
||||
@x.atomicNum = local_unnamed_addr global i32 0
|
||||
@x.volatileNum = global i32 0
|
||||
@y.ready = local_unnamed_addr global i32 0
|
||||
@z.bloom = global i64 0
|
||||
@z.arr = global [32 x i8] zeroinitializer
|
||||
|
||||
declare void @externalCall(i64) local_unnamed_addr
|
||||
|
||||
declare i64 @ptrHash(i8* nocapture) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call fastcc void @baz.init(i8* undef)
|
||||
@@ -24,6 +28,8 @@ entry:
|
||||
%y = load volatile i32, i32* @x.volatileNum, align 4
|
||||
store volatile i32 %y, i32* @x.volatileNum, align 4
|
||||
call fastcc void @y.init(i8* undef)
|
||||
call fastcc void @z.set(i8* bitcast (i64* @z.bloom to i8*), i8* bitcast (i64* @z.bloom to i8*))
|
||||
call fastcc void @z.setArr(i8* getelementptr inbounds ([32 x i8], [32 x i8]* @z.arr, i32 0, i32 0), i64 32, i8* bitcast (i64* @z.bloom to i8*))
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -48,3 +54,30 @@ loop: ; preds = %loop, %entry
|
||||
end: ; preds = %loop
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop: ; preds = %loop, %entry
|
||||
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||
%idx = sub i64 %prev, 1
|
||||
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||
call fastcc void @z.set(i8* %elem, i8* %context)
|
||||
%done = icmp eq i64 %idx, 0
|
||||
br i1 %done, label %end, label %loop
|
||||
|
||||
end: ; preds = %loop
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||
%hash = call i64 @ptrHash(i8* %ptr)
|
||||
%index = lshr i64 %hash, 58
|
||||
%bit = shl i64 1, %index
|
||||
%bloom = bitcast i8* %context to i64*
|
||||
%old = load i64, i64* %bloom, align 8
|
||||
%new = or i64 %old, %bit
|
||||
store i64 %new, i64* %bloom, align 8
|
||||
ret void
|
||||
}
|
||||
+1
-1
Submodule lib/wasi-libc updated: ad5133410f...30094b6ed0
+283
-13
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
@@ -15,16 +16,20 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/tinygo-org/tinygo/cgo"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var addInstances func(*types.Info)
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
config *compileopts.Config
|
||||
@@ -61,6 +66,9 @@ type PackageJSON struct {
|
||||
CgoFiles []string
|
||||
CFiles []string
|
||||
|
||||
// Embedded files
|
||||
EmbedFiles []string
|
||||
|
||||
// Dependency information
|
||||
Imports []string
|
||||
ImportMap map[string]string
|
||||
@@ -77,19 +85,28 @@ type PackageJSON struct {
|
||||
type Package struct {
|
||||
PackageJSON
|
||||
|
||||
program *Program
|
||||
Files []*ast.File
|
||||
FileHashes map[string][]byte
|
||||
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||
CGoHeaders []string // text above 'import "C"' lines
|
||||
Pkg *types.Package
|
||||
info types.Info
|
||||
program *Program
|
||||
Files []*ast.File
|
||||
FileHashes map[string][]byte
|
||||
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||
CGoHeaders []string // text above 'import "C"' lines
|
||||
EmbedGlobals map[string][]*EmbedFile
|
||||
Pkg *types.Package
|
||||
info types.Info
|
||||
}
|
||||
|
||||
type EmbedFile struct {
|
||||
Name string
|
||||
Size uint64
|
||||
Hash string // hash of the file (as a hex string)
|
||||
NeedsData bool // true if this file is embedded as a byte slice
|
||||
Data []byte // contents of this file (only if NeedsData is set)
|
||||
}
|
||||
|
||||
// Load loads the given package with all dependencies (including the runtime
|
||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||
// processing, if necessary).
|
||||
func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||
goroot, err := GetCachedGoroot(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -118,7 +135,7 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
if config.TestConfig.CompileTestBinary {
|
||||
extraArgs = append(extraArgs, "-test")
|
||||
}
|
||||
cmd, err := List(config, extraArgs, inputPkgs)
|
||||
cmd, err := List(config, extraArgs, []string{inputPkg})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -137,8 +154,9 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
decoder := json.NewDecoder(buf)
|
||||
for {
|
||||
pkg := &Package{
|
||||
program: p,
|
||||
FileHashes: make(map[string][]byte),
|
||||
program: p,
|
||||
FileHashes: make(map[string][]byte),
|
||||
EmbedGlobals: make(map[string][]*EmbedFile),
|
||||
info: types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
@@ -148,6 +166,9 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
},
|
||||
}
|
||||
if addInstances != nil {
|
||||
addInstances(&pkg.info)
|
||||
}
|
||||
err := decoder.Decode(&pkg.PackageJSON)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -357,15 +378,15 @@ func (p *Package) Check() error {
|
||||
return nil // already typechecked
|
||||
}
|
||||
|
||||
// Prepare some state used during type checking.
|
||||
var typeErrors []error
|
||||
checker := p.program.typeChecker // make a copy, because it will be modified
|
||||
checker.Error = func(err error) {
|
||||
typeErrors = append(typeErrors, err)
|
||||
}
|
||||
|
||||
// Do typechecking of the package.
|
||||
checker.Importer = p
|
||||
|
||||
// Do typechecking of the package.
|
||||
packageName := p.ImportPath
|
||||
if p == p.program.MainPkg() {
|
||||
if p.Name != "main" {
|
||||
@@ -382,6 +403,12 @@ func (p *Package) Check() error {
|
||||
return Errors{p, typeErrors}
|
||||
}
|
||||
p.Pkg = typesPkg
|
||||
|
||||
p.extractEmbedLines(checker.Error)
|
||||
if len(typeErrors) != 0 {
|
||||
return Errors{p, typeErrors}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -440,6 +467,249 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// extractEmbedLines finds all //go:embed lines in the package and matches them
|
||||
// against EmbedFiles from `go list`.
|
||||
func (p *Package) extractEmbedLines(addError func(error)) {
|
||||
for _, file := range p.Files {
|
||||
// Check for an `import "embed"` line at the start of the file.
|
||||
// //go:embed lines are only valid if the given file itself imports the
|
||||
// embed package. It is not valid if it is only imported in a separate
|
||||
// Go file.
|
||||
hasEmbed := false
|
||||
for _, importSpec := range file.Imports {
|
||||
if importSpec.Path.Value == `"embed"` {
|
||||
hasEmbed = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
if decl.Tok != token.VAR {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
var doc *ast.CommentGroup
|
||||
if decl.Lparen == token.NoPos {
|
||||
// Plain 'var' declaration, like:
|
||||
// //go:embed hello.txt
|
||||
// var hello string
|
||||
doc = decl.Doc
|
||||
} else {
|
||||
// Bigger 'var' declaration like:
|
||||
// var (
|
||||
// //go:embed hello.txt
|
||||
// hello string
|
||||
// )
|
||||
doc = spec.Doc
|
||||
}
|
||||
if doc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for //go:embed comments.
|
||||
var allPatterns []string
|
||||
for _, comment := range doc.List {
|
||||
if comment.Text != "//go:embed" && !strings.HasPrefix(comment.Text, "//go:embed ") {
|
||||
continue
|
||||
}
|
||||
if !hasEmbed {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: comment.Pos() + 2,
|
||||
Msg: "//go:embed only allowed in Go files that import \"embed\"",
|
||||
})
|
||||
// Continue, because otherwise we might run into
|
||||
// issues below.
|
||||
continue
|
||||
}
|
||||
patterns, err := p.parseGoEmbed(comment.Text[len("//go:embed"):], comment.Slash)
|
||||
if err != nil {
|
||||
addError(err)
|
||||
continue
|
||||
}
|
||||
if len(patterns) == 0 {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: comment.Pos() + 2,
|
||||
Msg: "usage: //go:embed pattern...",
|
||||
})
|
||||
continue
|
||||
}
|
||||
for _, pattern := range patterns {
|
||||
// Check that the pattern is well-formed.
|
||||
// It must be valid: the Go toolchain has already
|
||||
// checked for invalid patterns. But let's check
|
||||
// anyway to be sure.
|
||||
if _, err := path.Match(pattern, ""); err != nil {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: comment.Pos(),
|
||||
Msg: "invalid pattern syntax",
|
||||
})
|
||||
continue
|
||||
}
|
||||
allPatterns = append(allPatterns, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allPatterns) != 0 {
|
||||
// This is a //go:embed global. Do a few more checks.
|
||||
if len(spec.Names) != 1 {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: spec.Names[1].NamePos,
|
||||
Msg: "//go:embed cannot apply to multiple vars",
|
||||
})
|
||||
}
|
||||
if spec.Values != nil {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: spec.Values[0].Pos(),
|
||||
Msg: "//go:embed cannot apply to var with initializer",
|
||||
})
|
||||
}
|
||||
globalName := spec.Names[0].Name
|
||||
globalType := p.Pkg.Scope().Lookup(globalName).Type()
|
||||
valid, byteSlice := isValidEmbedType(globalType)
|
||||
if !valid {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: spec.Type.Pos(),
|
||||
Msg: "//go:embed cannot apply to var of type " + globalType.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// Match all //go:embed patterns against the embed files
|
||||
// provided by `go list`.
|
||||
for _, name := range p.EmbedFiles {
|
||||
for _, pattern := range allPatterns {
|
||||
if matchPattern(pattern, name) {
|
||||
p.EmbedGlobals[globalName] = append(p.EmbedGlobals[globalName], &EmbedFile{
|
||||
Name: name,
|
||||
NeedsData: byteSlice,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// matchPattern returns true if (and only if) the given pattern would match the
|
||||
// filename. The pattern could also match a parent directory of name, in which
|
||||
// case hidden files do not match.
|
||||
func matchPattern(pattern, name string) bool {
|
||||
// Match this file.
|
||||
matched, _ := path.Match(pattern, name)
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
|
||||
// Match parent directories.
|
||||
dir := name
|
||||
for {
|
||||
dir, _ = path.Split(dir)
|
||||
if dir == "" {
|
||||
return false
|
||||
}
|
||||
dir = path.Clean(dir)
|
||||
if matched, _ := path.Match(pattern, dir); matched {
|
||||
// Pattern matches the directory.
|
||||
suffix := name[len(dir):]
|
||||
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
|
||||
// Pattern matches a hidden file.
|
||||
// Hidden files are included when listed directly as a
|
||||
// pattern, but not when they are part of a directory tree.
|
||||
// Source:
|
||||
// > If a pattern names a directory, all files in the
|
||||
// > subtree rooted at that directory are embedded
|
||||
// > (recursively), except that files with names beginning
|
||||
// > with ‘.’ or ‘_’ are excluded.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
|
||||
// regular fields and quoted fields (that may contain spaces).
|
||||
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, err error) {
|
||||
args = strings.TrimSpace(args)
|
||||
initialLen := len(args)
|
||||
for args != "" {
|
||||
patternPos := pos + token.Pos(initialLen-len(args))
|
||||
switch args[0] {
|
||||
case '`', '"', '\\':
|
||||
// Parse the next pattern using the Go scanner.
|
||||
// This is perhaps a bit overkill, but it does correctly implement
|
||||
// parsing of the various Go strings.
|
||||
var sc scanner.Scanner
|
||||
fset := &token.FileSet{}
|
||||
file := fset.AddFile("", 0, len(args))
|
||||
sc.Init(file, []byte(args), nil, 0)
|
||||
_, tok, lit := sc.Scan()
|
||||
if tok != token.STRING || sc.ErrorCount != 0 {
|
||||
// Calculate start of token
|
||||
return nil, types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: patternPos,
|
||||
Msg: "invalid quoted string in //go:embed",
|
||||
}
|
||||
}
|
||||
pattern := constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
|
||||
patterns = append(patterns, pattern)
|
||||
args = strings.TrimLeftFunc(args[len(lit):], unicode.IsSpace)
|
||||
default:
|
||||
// The value is just a regular value.
|
||||
// Split it at the first white space.
|
||||
index := strings.IndexFunc(args, unicode.IsSpace)
|
||||
if index < 0 {
|
||||
index = len(args)
|
||||
}
|
||||
pattern := args[:index]
|
||||
patterns = append(patterns, pattern)
|
||||
args = strings.TrimLeftFunc(args[len(pattern):], unicode.IsSpace)
|
||||
}
|
||||
if _, err := path.Match(patterns[len(patterns)-1], ""); err != nil {
|
||||
return nil, types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: patternPos,
|
||||
Msg: "invalid pattern syntax",
|
||||
}
|
||||
}
|
||||
}
|
||||
return patterns, nil
|
||||
}
|
||||
|
||||
// isValidEmbedType returns whether the given Go type can be used as a
|
||||
// //go:embed type. This is only true for embed.FS, strings, and byte slices.
|
||||
// The second return value indicates that this is a byte slice, and therefore
|
||||
// the contents of the file needs to be passed to the compiler.
|
||||
func isValidEmbedType(typ types.Type) (valid, byteSlice bool) {
|
||||
if typ.Underlying() == types.Typ[types.String] {
|
||||
// string type
|
||||
return true, false
|
||||
}
|
||||
if sliceType, ok := typ.Underlying().(*types.Slice); ok {
|
||||
if elemType, ok := sliceType.Elem().Underlying().(*types.Basic); ok && elemType.Kind() == types.Byte {
|
||||
// byte slice type
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
if namedType, ok := typ.(*types.Named); ok && namedType.String() == "embed.FS" {
|
||||
// embed.FS type
|
||||
return true, false
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Import implements types.Importer. It loads and parses packages it encounters
|
||||
// along the way, if needed.
|
||||
func (p *Package) Import(to string) (*types.Package, error) {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package loader
|
||||
|
||||
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||
// support.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
addInstances = func(info *types.Info) {
|
||||
info.Instances = make(map[*ast.Ident]types.Instance)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
//
|
||||
// The program must already be parsed and type-checked with the .Parse() method.
|
||||
func (p *Program) LoadSSA() *ssa.Program {
|
||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
|
||||
|
||||
for _, pkg := range p.sorted {
|
||||
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
@@ -1252,6 +1253,35 @@ func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
|
||||
return map[string]map[string]string(globalVarValues), nil
|
||||
}
|
||||
|
||||
// getListOfPackages returns a standard list of packages for a given list that might
|
||||
// include wildards using `go list`.
|
||||
// For example [./...] => ["pkg1", "pkg1/pkg12", "pkg2"]
|
||||
func getListOfPackages(pkgs []string, options *compileopts.Options) ([]string, error) {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := loader.List(config, nil, pkgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run `go list`: %w", err)
|
||||
}
|
||||
outputBuf := bytes.NewBuffer(nil)
|
||||
cmd.Stdout = outputBuf
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pkgNames []string
|
||||
sc := bufio.NewScanner(outputBuf)
|
||||
for sc.Scan() {
|
||||
pkgNames = append(pkgNames, sc.Text())
|
||||
}
|
||||
|
||||
return pkgNames, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
@@ -1487,14 +1517,21 @@ func main() {
|
||||
if len(pkgNames) == 0 {
|
||||
pkgNames = []string{"."}
|
||||
}
|
||||
if outpath != "" && len(pkgNames) > 1 {
|
||||
|
||||
explicitPkgNames, err := getListOfPackages(pkgNames, options)
|
||||
if err != nil {
|
||||
fmt.Printf("cannot resolve packages: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if outpath != "" && len(explicitPkgNames) > 1 {
|
||||
fmt.Println("cannot use -o flag with multiple packages")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fail := make(chan struct{}, 1)
|
||||
var wg sync.WaitGroup
|
||||
bufs := make([]testOutputBuf, len(pkgNames))
|
||||
bufs := make([]testOutputBuf, len(explicitPkgNames))
|
||||
for i := range bufs {
|
||||
bufs[i].done = make(chan struct{})
|
||||
}
|
||||
@@ -1520,7 +1557,7 @@ func main() {
|
||||
// Build and run the tests concurrently.
|
||||
// This uses an additional semaphore to reduce the memory usage.
|
||||
testSema := make(chan struct{}, cap(options.Semaphore))
|
||||
for i, pkgName := range pkgNames {
|
||||
for i, pkgName := range explicitPkgNames {
|
||||
pkgName := pkgName
|
||||
buf := &bufs[i]
|
||||
testSema <- struct{}{}
|
||||
|
||||
+83
-40
@@ -13,6 +13,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -48,6 +49,7 @@ func TestBuild(t *testing.T) {
|
||||
"calls.go",
|
||||
"cgo/",
|
||||
"channel.go",
|
||||
"embed/",
|
||||
"float.go",
|
||||
"gc.go",
|
||||
"goroutines.go",
|
||||
@@ -74,6 +76,7 @@ func TestBuild(t *testing.T) {
|
||||
tests = append(tests, "go1.17.go")
|
||||
}
|
||||
if minor >= 18 {
|
||||
tests = append(tests, "generics.go")
|
||||
tests = append(tests, "testing_go118.go")
|
||||
} else {
|
||||
tests = append(tests, "testing.go")
|
||||
@@ -146,46 +149,7 @@ func TestBuild(t *testing.T) {
|
||||
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
|
||||
// This bug is non-deterministic.
|
||||
t.Skip("skipped due to non-deterministic backend bugs")
|
||||
|
||||
var avrTests []string
|
||||
for _, t := range tests {
|
||||
switch t {
|
||||
case "atomic.go":
|
||||
// Requires GCC 11.2.0 or above for interface comparison.
|
||||
// https://github.com/gcc-mirror/gcc/commit/f30dd607669212de135dec1f1d8a93b8954c327c
|
||||
|
||||
case "reflect.go":
|
||||
// Reflect tests do not work due to type code issues.
|
||||
|
||||
case "gc.go":
|
||||
// Does not pass due to high mark false positive rate.
|
||||
|
||||
case "json.go", "stdlib.go", "testing.go":
|
||||
// Breaks interp.
|
||||
|
||||
case "map.go":
|
||||
// Reflect size calculation crashes.
|
||||
|
||||
case "binop.go":
|
||||
// Interface comparison results are inverted.
|
||||
|
||||
case "channel.go":
|
||||
// Freezes after recv from closed channel.
|
||||
|
||||
case "float.go", "math.go", "print.go":
|
||||
// Stuck in runtime.printfloat64.
|
||||
|
||||
case "interface.go":
|
||||
// Several comparison tests fail.
|
||||
|
||||
case "cgo/":
|
||||
// CGo does not work on AVR.
|
||||
|
||||
default:
|
||||
avrTests = append(avrTests, t)
|
||||
}
|
||||
}
|
||||
runPlatTests(optionsFromTarget("simavr", sema), avrTests, t)
|
||||
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
||||
})
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
@@ -217,6 +181,37 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
}
|
||||
|
||||
for _, name := range tests {
|
||||
if options.Target == "simavr" {
|
||||
// Not all tests are currently supported on AVR.
|
||||
// Skip the ones that aren't.
|
||||
switch name {
|
||||
case "reflect.go":
|
||||
// Reflect tests do not work due to type code issues.
|
||||
continue
|
||||
|
||||
case "gc.go":
|
||||
// Does not pass due to high mark false positive rate.
|
||||
continue
|
||||
|
||||
case "json.go", "stdlib.go", "testing.go", "testing_go118.go":
|
||||
// Breaks interp.
|
||||
continue
|
||||
|
||||
case "channel.go":
|
||||
// Freezes after recv from closed channel.
|
||||
continue
|
||||
|
||||
case "math.go":
|
||||
// Stuck somewhere, not sure what's happening.
|
||||
continue
|
||||
|
||||
case "cgo/":
|
||||
// CGo does not work on AVR.
|
||||
continue
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
name := name // redefine to avoid race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -249,6 +244,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
runTest("rand.go", options, t, nil, nil)
|
||||
})
|
||||
}
|
||||
if options.Target != "wasi" && options.Target != "wasm" {
|
||||
// The recover() builtin isn't supported yet on WebAssembly and Windows.
|
||||
t.Run("recover.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("recover.go", options, t, nil, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func emuCheck(t *testing.T, options compileopts.Options) {
|
||||
@@ -347,6 +349,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
||||
actual = bytes.Replace(actual, []byte{'.', '.', '\n'}, []byte{'\n'}, -1)
|
||||
actual = bytes.Replace(actual, []byte{'\n', '.', '\n'}, []byte{'\n', '\n'}, -1)
|
||||
}
|
||||
if name == "testing.go" {
|
||||
// Strip actual time.
|
||||
@@ -527,6 +530,46 @@ func ioLogger(t *testing.T, wg *sync.WaitGroup) io.WriteCloser {
|
||||
return w
|
||||
}
|
||||
|
||||
func TestGetListOfPackages(t *testing.T) {
|
||||
opts := optionsFromTarget("", sema)
|
||||
tests := []struct {
|
||||
pkgs []string
|
||||
expectedPkgs []string
|
||||
expectesError bool
|
||||
}{
|
||||
{
|
||||
pkgs: []string{"./tests/testing/recurse/..."},
|
||||
expectedPkgs: []string{
|
||||
"github.com/tinygo-org/tinygo/tests/testing/recurse",
|
||||
"github.com/tinygo-org/tinygo/tests/testing/recurse/subdir",
|
||||
},
|
||||
},
|
||||
{
|
||||
pkgs: []string{"./tests/testing/pass"},
|
||||
expectedPkgs: []string{
|
||||
"github.com/tinygo-org/tinygo/tests/testing/pass",
|
||||
},
|
||||
},
|
||||
{
|
||||
pkgs: []string{"./tests/testing"},
|
||||
expectesError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
actualPkgs, err := getListOfPackages(test.pkgs, &opts)
|
||||
if err != nil && !test.expectesError {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if err == nil && test.expectesError {
|
||||
t.Error("expected error, but got none")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedPkgs, actualPkgs) {
|
||||
t.Errorf("expected two slices to be equal, expected %v got %v", test.expectedPkgs, actualPkgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||
// to recursive tests.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build darwin || tinygo.wasm
|
||||
// +build darwin tinygo.wasm
|
||||
|
||||
// This implementation of crypto/rand uses the arc4random_buf function
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build linux && !baremetal && !wasi
|
||||
// +build linux,!baremetal,!wasi
|
||||
|
||||
// This implementation of crypto/rand uses the /dev/urandom pseudo-file to
|
||||
|
||||
@@ -193,12 +193,12 @@ func enableIcache(enable bool) {
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
} else {
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
|
||||
SystemControl.ICIALLU.Set(0)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// This is a echo console running on the os.Stdin and os.Stdout.
|
||||
// Stdin and os.Stdout are connected to machine.Serial in the baremetal target.
|
||||
//
|
||||
// Serial can be switched with the -serial option as follows
|
||||
// 1. tinygo flash -target wioterminal -serial usb examples/echo2
|
||||
// 2. tinygo flash -target wioterminal -serial uart examples/echo2
|
||||
//
|
||||
// This example will also work with standard Go.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Echo console enabled. Type something then press enter:\r\n")
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
for {
|
||||
msg := ""
|
||||
fmt.Scanf("%s\n", &msg)
|
||||
fmt.Printf("You typed (scanf) : %s\r\n", msg)
|
||||
|
||||
if scanner.Scan() {
|
||||
fmt.Printf("You typed (scanner) : %s\r\n", scanner.Text())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb/hid/keyboard"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
kb := keyboard.New()
|
||||
|
||||
for {
|
||||
if !button.Get() {
|
||||
kb.Write([]byte("tinygo"))
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb/hid/mouse"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
mouse := mouse.New()
|
||||
|
||||
for {
|
||||
if !button.Get() {
|
||||
for j := 0; j < 5; j++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(1, 0)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(0, 1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(-1, -1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// This reads from UART1 and outputs to default serial, usually UART0 or USB.
|
||||
// Example of how to work with UARTs other than the default.
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
uart = machine.UART1
|
||||
tx = machine.UART1_TX_PIN
|
||||
rx = machine.UART1_RX_PIN
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
for {
|
||||
if uart.Buffered() > 0 {
|
||||
data, _ := uart.ReadByte()
|
||||
print(string(data))
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func CheckCorpus(vals []any, types []reflect.Type) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func ResetCoverage() {}
|
||||
func ResetCoverage() {}
|
||||
func SnapshotCoverage() {}
|
||||
|
||||
// RunFuzzWorker is called in a worker process to communicate with the
|
||||
|
||||
@@ -20,6 +20,10 @@ type Task struct {
|
||||
|
||||
// state is the underlying running state of the task.
|
||||
state state
|
||||
|
||||
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
|
||||
// goroutine that is used for the recover builtin.
|
||||
DeferFrame unsafe.Pointer
|
||||
}
|
||||
|
||||
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
//go:build scheduler.none
|
||||
// +build scheduler.none
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// There is only one goroutine so the task struct can be a global.
|
||||
var mainTask Task
|
||||
|
||||
//go:linkname runtimePanic runtime.runtimePanic
|
||||
func runtimePanic(str string)
|
||||
|
||||
@@ -12,8 +16,8 @@ func Pause() {
|
||||
}
|
||||
|
||||
func Current() *Task {
|
||||
runtimePanic("scheduler is disabled")
|
||||
return nil
|
||||
// Return a task struct, which is used for the recover builtin for example.
|
||||
return &mainTask
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build scheduler.tasks && 386
|
||||
// +build scheduler.tasks,386
|
||||
|
||||
package task
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build scheduler.tasks && amd64 && !windows
|
||||
// +build scheduler.tasks,amd64,!windows
|
||||
|
||||
package task
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build scheduler.tasks && amd64 && windows
|
||||
// +build scheduler.tasks,amd64,windows
|
||||
|
||||
package task
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build scheduler.tasks && arm && !cortexm && !avr && !xtensa && !tinygo.riscv
|
||||
// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa,!tinygo.riscv
|
||||
|
||||
package task
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#ifdef __MACH__
|
||||
.global _tinygo_startTask
|
||||
_tinygo_startTask:
|
||||
#else
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
#endif
|
||||
.cfi_startproc
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
@@ -23,13 +28,25 @@ tinygo_startTask:
|
||||
blr x19
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
#ifdef __MACH__
|
||||
b _tinygo_pause
|
||||
#else
|
||||
b tinygo_pause
|
||||
#endif
|
||||
.cfi_endproc
|
||||
#ifndef __MACH__
|
||||
.size tinygo_startTask, .-tinygo_startTask
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __MACH__
|
||||
.global _tinygo_swapTask
|
||||
_tinygo_swapTask:
|
||||
#else
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
#endif
|
||||
// This function gets the following parameters:
|
||||
// x0 = newStack uintptr
|
||||
// x1 = oldStack *uintptr
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build scheduler.tasks && arm64
|
||||
// +build scheduler.tasks,arm64
|
||||
|
||||
package task
|
||||
|
||||
@@ -72,7 +72,10 @@ tinygo_swapTask:
|
||||
std Y+1, r3
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
in r0, 0x3f ; SREG
|
||||
cli
|
||||
out 0x3d, r24; SPL
|
||||
out 0x3f, r0 ; SREG, restore interrupts (after the next instruction)
|
||||
out 0x3e, r25; SPH
|
||||
|
||||
// Load saved register from the new stack.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build scheduler.tasks && avr
|
||||
// +build scheduler.tasks,avr
|
||||
|
||||
package task
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user