Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem 3b68f8f16a machine: hide internal pin modes
Many pin modes are only for peripherals like SPI and PWM. Do not make
these part of the public API: the public API is the Configure method on
the peripheral, not the PinMode constant.
2022-09-26 18:27:55 +02:00
661 changed files with 4212 additions and 8295 deletions
+14
View File
@@ -6,6 +6,18 @@ commands:
- run: - run:
name: "Pull submodules" name: "Pull submodules"
command: git submodule update --init command: git submodule update --init
install-xtensa-toolchain:
parameters:
variant:
type: string
steps:
- run:
name: "Install Xtensa toolchain"
command: |
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
llvm-source-linux: llvm-source-linux:
steps: steps:
- restore_cache: - restore_cache:
@@ -63,6 +75,8 @@ commands:
clang-<<parameters.llvm>> \ clang-<<parameters.llvm>> \
libclang-<<parameters.llvm>>-dev \ libclang-<<parameters.llvm>>-dev \
lld-<<parameters.llvm>> \ lld-<<parameters.llvm>> \
gcc-avr \
avr-libc \
cmake \ cmake \
ninja-build ninja-build
- hack-ninja-jobs - hack-ninja-jobs
+15 -8
View File
@@ -20,20 +20,27 @@ jobs:
shell: bash shell: bash
run: | run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
- name: Install Xtensa toolchain
shell: bash
run: |
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-macos-v2 key: llvm-source-14-macos-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -47,7 +54,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-macos-v3 key: llvm-build-14-macos-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -93,7 +100,7 @@ jobs:
path: build/tinygo.darwin-amd64.tar.gz path: build/tinygo.darwin-amd64.tar.gz
- name: Smoke tests - name: Smoke tests
shell: bash shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0
test-macos-homebrew: test-macos-homebrew:
name: homebrew-install name: homebrew-install
runs-on: macos-latest runs-on: macos-latest
@@ -101,13 +108,13 @@ jobs:
- name: Install LLVM - name: Install LLVM
shell: bash shell: bash
run: | run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15 HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@14
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Build TinyGo - name: Build TinyGo
run: go install run: go install
+6 -6
View File
@@ -20,14 +20,14 @@ jobs:
contents: read contents: read
steps: steps:
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v1
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v4 uses: docker/metadata-action@v3
with: with:
images: | images: |
tinygo/tinygo-dev tinygo/tinygo-dev
@@ -36,18 +36,18 @@ jobs:
type=sha,format=long type=sha,format=long
type=raw,value=latest type=raw,value=latest
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v2 uses: docker/login-action@v1
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Log in to Github Container Registry - name: Log in to Github Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v1
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v3 uses: docker/build-push-action@v2
with: with:
context: . context: .
push: true push: true
+44 -25
View File
@@ -18,7 +18,7 @@ jobs:
# statically linked binary. # statically linked binary.
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: golang:1.20-alpine image: golang:1.19-alpine
steps: steps:
- name: Install apk dependencies - name: Install apk dependencies
# tar: needed for actions/cache@v3 # tar: needed for actions/cache@v3
@@ -29,7 +29,7 @@ jobs:
# We're not on a multi-user machine, so this is safe. # We're not on a multi-user machine, so this is safe.
run: git config --global --add safe.directory "$GITHUB_WORKSPACE" run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Cache Go - name: Cache Go
@@ -43,7 +43,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-alpine-v2 key: llvm-source-14-linux-alpine-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -57,7 +57,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-alpine-v3 key: llvm-build-14-linux-alpine-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -102,7 +102,7 @@ jobs:
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb cp -p build/release.deb /tmp/tinygo_amd64.deb
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: linux-amd64-double-zipped name: linux-amd64-double-zipped
path: | path: |
@@ -114,18 +114,18 @@ jobs:
needs: build-linux needs: build-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Install wasmtime - name: Install wasmtime
run: | run: |
curl https://wasmtime.dev/install.sh -sSf | bash curl https://wasmtime.dev/install.sh -sSf | bash
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Download release artifact - name: Download release artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v2
with: with:
name: linux-amd64-double-zipped name: linux-amd64-double-zipped
- name: Extract release tarball - name: Extract release tarball
@@ -133,6 +133,17 @@ jobs:
mkdir -p ~/lib mkdir -p ~/lib
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
gcc-avr \
avr-libc
- name: "Install Xtensa toolchain"
run: |
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
- run: make tinygo-test-wasi-fast - run: make tinygo-test-wasi-fast
- run: make smoketest - run: make smoketest
assert-test-linux: assert-test-linux:
@@ -141,7 +152,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Install apt dependencies - name: Install apt dependencies
@@ -153,15 +164,17 @@ jobs:
qemu-system-arm \ qemu-system-arm \
qemu-system-riscv32 \ qemu-system-riscv32 \
qemu-user \ qemu-user \
gcc-avr \
avr-libc \
simavr \ simavr \
ninja-build ninja-build
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v2
with: with:
node-version: '14' node-version: '14'
- name: Install wasmtime - name: Install wasmtime
@@ -172,7 +185,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-asserts-v2 key: llvm-source-14-linux-asserts-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -186,7 +199,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-asserts-v3 key: llvm-build-14-linux-asserts-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -225,6 +238,12 @@ jobs:
echo "$(pwd)/build" >> $GITHUB_PATH echo "$(pwd)/build" >> $GITHUB_PATH
- name: Test stdlib packages - name: Test stdlib packages
run: make tinygo-test run: make tinygo-test
- name: Install Xtensa toolchain
run: |
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
- run: make smoketest - run: make smoketest
- run: make wasmtest - run: make wasmtest
- run: make tinygo-baremetal - run: make tinygo-baremetal
@@ -241,7 +260,7 @@ jobs:
needs: build-linux needs: build-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install apt dependencies - name: Install apt dependencies
run: | run: |
sudo apt-get update sudo apt-get update
@@ -252,13 +271,13 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-v2 key: llvm-source-14-linux-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -272,7 +291,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-arm-v3 key: llvm-build-14-linux-arm-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -307,7 +326,7 @@ jobs:
run: | run: |
make CROSS=arm-linux-gnueabihf make CROSS=arm-linux-gnueabihf
- name: Download amd64 release - name: Download amd64 release
uses: actions/download-artifact@v3 uses: actions/download-artifact@v2
with: with:
name: linux-amd64-double-zipped name: linux-amd64-double-zipped
- name: Extract amd64 release - name: Extract amd64 release
@@ -324,7 +343,7 @@ jobs:
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
cp -p build/release.deb /tmp/tinygo_armhf.deb cp -p build/release.deb /tmp/tinygo_armhf.deb
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: linux-arm-double-zipped name: linux-arm-double-zipped
path: | path: |
@@ -340,7 +359,7 @@ jobs:
needs: build-linux needs: build-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install apt dependencies - name: Install apt dependencies
run: | run: |
sudo apt-get update sudo apt-get update
@@ -352,13 +371,13 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-v2 key: llvm-source-14-linux-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -372,7 +391,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-arm64-v3 key: llvm-build-14-linux-arm64-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -404,7 +423,7 @@ jobs:
run: | run: |
make CROSS=aarch64-linux-gnu make CROSS=aarch64-linux-gnu
- name: Download amd64 release - name: Download amd64 release
uses: actions/download-artifact@v3 uses: actions/download-artifact@v2
with: with:
name: linux-amd64-double-zipped name: linux-amd64-double-zipped
- name: Extract amd64 release - name: Extract amd64 release
@@ -421,7 +440,7 @@ jobs:
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
cp -p build/release.deb /tmp/tinygo_arm64.deb cp -p build/release.deb /tmp/tinygo_arm64.deb
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: linux-arm64-double-zipped name: linux-arm64-double-zipped
path: | path: |
+8 -85
View File
@@ -23,19 +23,19 @@ jobs:
run: | run: |
scoop install ninja binaryen scoop install ninja binaryen
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-windows-v2 key: llvm-source-14-windows-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -49,7 +49,7 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-windows-v3 key: llvm-build-14-windows-v2
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -91,91 +91,14 @@ jobs:
# - have a dobule-zipped artifact when downloaded from the UI # - have a dobule-zipped artifact when downloaded from the UI
# - have a very slow artifact upload # - have a very slow artifact upload
# We're doing the former here, to keep artifact uploads fast. # We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: release-double-zipped name: release-double-zipped
path: build/release/release.zip path: build/release/release.zip
smoke-test-windows:
runs-on: windows-2022
needs: build-windows
steps:
- uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies
shell: bash
run: |
scoop install binaryen
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
with:
name: release-double-zipped
path: build/
- name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
- name: Smoke tests - name: Smoke tests
shell: bash shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
stdlib-test-windows:
runs-on: windows-2022
needs: build-windows
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
with:
name: release-double-zipped
path: build/
- name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
- name: Test stdlib packages - name: Test stdlib packages
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo run: make tinygo-test
stdlib-wasi-test-windows:
runs-on: windows-2022
needs: build-windows
steps:
- uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies
shell: bash
run: |
scoop install binaryen wasmtime
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
with:
name: release-double-zipped
path: build/
- name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
- name: Test stdlib packages on wasi - name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo run: make tinygo-test-wasi-fast
-181
View File
@@ -1,184 +1,3 @@
0.27.0
---
* **general**
- all: update musl
- all: remove "acm:"` prefix for USB vid/pid pair
- all: add support for LLVM 15
- all: use DWARF version 4
- all: add initial (incomplete) support for Go 1.20
- all: add `-gc=custom` option
- `main`: print ldflags including ThinLTO flags with -x
- `main`: fix error message when a serial port can't be accessed
- `main`: add `-timeout` flag to allow setting how long TinyGo will try looking for a MSD volume for flashing
- `test`: print PASS on pass when running standalone test binaries
- `test`: fix printing of benchmark output
- `test`: print package name when compilation failed (not just when the test failed)
* **compiler**
- refactor to support LLVM 15
- `builder`: print compiler commands while building a library
- `compiler`: fix stack overflow when creating recursive pointer types (fix for LLVM 15+ only)
- `compiler`: allow map keys and values of ≥256 bytes
- `cgo`: add support for `C.float` and `C.double`
- `cgo`: support anonymous enums included in multiple Go files
- `cgo`: add support for bitwise operators
- `interp`: add support for constant icmp instructions
- `transform`: fix memory corruption issues
* **standard library**
- `machine/usb`: remove allocs in USB ISR
- `machine/usb`: add `Port()` and deprecate `New()` to have the API better match the singleton that is actually being returned
- `machine/usb`: change HID usage-maximum to 0xFF
- `machine/usb`: add USB HID joystick support
- `machine/usb`: change to not send before endpoint initialization
- `net`: implement `Pipe`
- `os`: add stub for `os.Chtimes`
- `reflect`: stub out `Type.FieldByIndex`
- `reflect`: add `Value.IsZero` method
- `reflect`: fix bug in `.Field` method when the field fits in a pointer but the parent doesn't
- `runtime`: switch some `panic()` calls in the gc to `runtimePanic()` for consistency
- `runtime`: add xorshift-based fastrand64
- `runtime`: fix alignment for arm64, arm, xtensa, riscv
- `runtime`: implement precise GC
- `runtime/debug`: stub `PrintStack`
- `sync`: implement simple pooling in `sync.Pool`
- `syscall`: stubbed `Setuid`, Exec and friends
- `syscall`: add more stubs as needed for Go 1.20 support
- `testing`: implement `t.Setenv`
- `unsafe`: add support for Go 1.20 slice/string functions
* **targets**
- `all`: do not set stack size per board
- `all`: update picolibc to v1.7.9
- `atsame5x`: fix CAN extendedID handling
- `atsame5x`: reduce heap allocation
- `avr`: drop GNU toolchain dependency
- `avr`: fix .data initialization for binaries over 64kB
- `avr`: support ThinLTO
- `baremetal`: implements calloc
- `darwin`: fix `syscall.Open` on darwin/arm64
- `darwin`: fix error with `tinygo lldb`
- `esp`: use LLVM Xtensa linker instead of Espressif toolchain
- `esp`: use ThinLTO for Xtensa
- `esp32c3`: add SPI support
- `linux`: include musl `getpagesize` function in release
- `nrf51`: add ADC implementation
- `nrf52840`: add PDM support
- `riscv`: add "target-abi" metadata flag
- `rp2040`: remove mem allocation in GPIO ISR
- `rp2040`: avoid allocating clock on heap
- `rp2040`: add basic GPIO support for PIO
- `rp2040`: fix USB interrupt issue
- `rp2040`: fix RP2040-E5 USB errata
- `stm32`: always set ADC pins to pullups floating
- `stm32f1`, `stm32f4`: fix ADC by clearing the correct bit for rank after each read
- `stm32wl`: Fix incomplete RNG initialisation
- `stm32wlx`: change order for init so clock speeds are set before peripheral start
- `wasi`: makes wasmtime "run" explicit
- `wasm`: fix GC scanning of allocas
- `wasm`: allow custom malloc implementation
- `wasm`: remove `-wasm-abi=` flag (use `-target` instead)
- `wasm`: fix scanning of the stack
- `wasm`: fix panic when allocating 0 bytes using malloc
- `wasm`: always run wasm-opt even with `-scheduler=none`
- `wasm`: avoid miscompile with ThinLTO
- `wasm`: allow the emulator to expand `{tmpDir}`
- `wasm`: support ThinLTO
- `windows`: update mingw-w64 version to avoid linker warning
- `windows`: add ARM64 support
* **boards**
- Add Waveshare RP2040 Zero
- Add Arduino Leonardo support
- Add Adafruit KB2040
- Add Adafruit Feather M0 Express
- Add Makerfabs ESP32C3SPI35 TFT Touchscreen board
- Add Espressif ESP32-C3-DevKit-RUST-1 board
- `lgt92`: fix OpenOCD configuration
- `xiao-rp2040`: fix D9 and D10 constants
- `xiao-rp2040`: add pin definitions
0.26.0
---
* **general**
- remove support for LLVM 13
- remove calls to deprecated ioutil package
- move from `os.IsFoo` to `errors.Is(err, ErrFoo)`
- fix for builds using an Android host
- make interp timeout configurable from command line
- ignore ports with VID/PID if there is no candidates
- drop support for Go 1.16 and Go 1.17
- update serial package to v1.3.5 for latest bugfixes
- remove GOARM from `tinygo info`
- add flag for setting the goroutine stack size
- add serial port monitoring functionality
* **compiler**
- `cgo`: implement support for static functions
- `cgo`: fix panic when FuncType.Results is nil
- `compiler`: add aliases for `edwards25519/field.feMul` and `field.feSquare`
- `compiler`: fix incorrect DWARF type in some generic parameters
- `compiler`: use LLVM math builtins everywhere
- `compiler`: replace some math operation bodies with LLVM intrinsics
- `compiler`: replace math aliases with intrinsics
- `compiler`: fix `unsafe.Sizeof` for chan and map values
- `compileopts`: use tags parser from buildutil
- `compileopts`: use backticks for regexp to avoid extra escapes
- `compileopts`: fail fast on duplicate values in target field slices
- `compileopts`: fix windows/arm target triple
- `compileopts`: improve error handling when loading target/*.json
- `compileopts`: add support for stlink-dap programmer
- `compileopts`: do not complain about `-no-debug` on MacOS
- `goenv`: support `GOOS=android`
- `interp`: fix reading from external global
- `loader`: fix link error for `crypto/internal/boring/sig.StandardCrypto`
* **standard library**
- rename assembly files to .S extension
- `machine`: add PWM peripheral comments to pins
- `machine`: improve UARTParity slightly
- `machine`: do not export DFU_MAGIC_* constants on nrf52840
- `machine`: rename `PinInputPullUp`/`PinInputPullDown`
- `machine`: add `KHz`, `MHz`, `GHz` constants, deprecate `TWI_FREQ_*` constants
- `machine`: remove level triggered pin interrupts
- `machine`: do not expose `RESET_MAGIC_VALUE`
- `machine`: use `NoPin` constant where appropriate (instead of `0` for example)
- `net`: sync net.go with Go 1.18 stdlib
- `os`: add `SyscallError.Timeout`
- `os`: add `ErrProcessDone` error
- `reflect`: implement `CanInterface` and fix string `Index`
- `runtime`: make `MemStats` available to leaking collector
- `runtime`: add `MemStats.TotalAlloc`
- `runtime`: add `MemStats.Mallocs` and `Frees`
- `runtime`: add support for `time.NewTimer` and `time.NewTicker`
- `runtime`: implement `resetTimer`
- `runtime`: ensure some headroom for the GC to run
- `runtime`: make gc and scheduler asserts settable with build tags
- `runtime/pprof`: add `WriteHeapProfile`
- `runtime/pprof`: `runtime/trace`: stub some additional functions
- `sync`: implement `Map.LoadAndDelete`
- `syscall`: group WASI consts by purpose
- `syscall`: add WASI `{D,R}SYNC`, `NONBLOCK` FD flags
- `syscall`: add ENOTCONN on darwin
- `testing`: add support for -benchmem
* **targets**
- remove USB vid/pid pair of bootloader
- `esp32c3`: remove unused `UARTStopBits` constants
- `nrf`: implement `GetRNG` function
- `nrf`: `rp2040`: add `machine.ReadTemperature`
- `nrf52`: cleanup s140v6 and s140v7 uf2 targets
- `rp2040`: implement semi-random RNG based on ROSC based on pico-sdk
- `wasm`: add summary of wasm examples and fix callback bug
- `wasm`: do not allow undefined symbols (`--allow-undefined`)
- `wasm`: make sure buffers returned by `malloc` are kept until `free` is called
- `windows`: save and restore xmm registers when switching goroutines
* **boards**
- add Pimoroni's Tufty2040
- add XIAO ESP32C3
- add Adafruit QT2040
- add Adafruit QT Py RP2040
- `esp32c3-12f`: `matrixportal-m4`: `p1am-100`: remove duplicate build tags
- `hifive1-qemu`: remove this emulated board
- `wioterminal`: add UART3 for RTL8720DN
- `xiao-ble`: fix usbpid
0.25.0 0.25.0
--- ---
+13 -3
View File
@@ -1,8 +1,8 @@
# tinygo-llvm stage obtains the llvm source for TinyGo # tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.20 AS tinygo-llvm FROM golang:1.19 AS tinygo-llvm
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-11 ninja-build apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
COPY ./Makefile /tinygo/Makefile COPY ./Makefile /tinygo/Makefile
@@ -15,8 +15,18 @@ FROM tinygo-llvm AS tinygo-llvm-build
RUN cd /tinygo/ && \ RUN cd /tinygo/ && \
make llvm-build make llvm-build
# tinygo-xtensa stage installs tools needed for ESP32
FROM tinygo-llvm-build AS tinygo-xtensa
ARG xtensa_version="1.22.0-80-g6c4433a-5.2.0"
RUN cd /tmp/ && \
wget -q https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-${xtensa_version}.tar.gz && \
tar xzf xtensa-esp32-elf-linux64-${xtensa_version}.tar.gz && \
cp ./xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/ && \
rm -rf /tmp/xtensa*
# tinygo-compiler stage builds the compiler itself # tinygo-compiler stage builds the compiler itself
FROM tinygo-llvm-build AS tinygo-compiler FROM tinygo-xtensa AS tinygo-compiler
COPY . /tinygo COPY . /tinygo
+9 -20
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools. # Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order. # Versions are listed here in descending priority order.
LLVM_VERSIONS = 15 14 13 12 11 LLVM_VERSIONS = 14 13 12 11
errifempty = $(if $(1),$(1),$(error $(2))) errifempty = $(if $(1),$(1),$(error $(2)))
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2))) detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
toolSearchPathsVersion = $(1)-$(2) toolSearchPathsVersion = $(1)-$(2)
@@ -56,12 +56,6 @@ else
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF' LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
endif endif
# Enable AddressSanitizer
ifeq (1, $(ASAN))
LLVM_OPTION += -DLLVM_USE_SANITIZER=Address
CGO_LDFLAGS += -fsanitize=address
endif
ifeq (1, $(STATIC)) ifeq (1, $(STATIC))
# Build TinyGo as a fully statically linked binary (no dynamically loaded # Build TinyGo as a fully statically linked binary (no dynamically loaded
# libraries such as a libc). This is not supported with glibc which is used # libraries such as a libc). This is not supported with glibc which is used
@@ -113,7 +107,7 @@ endif
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp .PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
EXE = .exe EXE = .exe
@@ -148,7 +142,7 @@ else
endif endif
# Libraries that should be linked in for the statically linked Clang. # Libraries that should be linked in for the statically linked Clang.
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++ CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD. # Libraries that should be linked in for the statically linked LLD.
@@ -236,13 +230,13 @@ gen-device-rp: build/gen-device-svd
# Get LLVM sources. # Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR) git clone -b xtensa_release_14.0.0-patched --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: $(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_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION) mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
# Build LLVM. # Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
@@ -263,7 +257,7 @@ endif
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
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 @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 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) 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. # Build the Go compiler.
@@ -286,6 +280,7 @@ TEST_PACKAGES_FAST = \
container/list \ container/list \
container/ring \ container/ring \
crypto/des \ crypto/des \
crypto/internal/subtle \
crypto/md5 \ crypto/md5 \
crypto/rc4 \ crypto/rc4 \
crypto/sha1 \ crypto/sha1 \
@@ -611,8 +606,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=qtpy-rp2040 examples/echo $(TINYGO) build -size short -o test.hex -target=qtpy-rp2040 examples/echo
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=kb2040 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
@@ -623,8 +616,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-zero examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=trinkey-qt2040 examples/temp $(TINYGO) build -size short -o test.hex -target=trinkey-qt2040 examples/temp
@@ -677,12 +668,11 @@ ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1 $(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
endif endif
ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial $(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-leonardo examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm $(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
@@ -697,6 +687,7 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1 $(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
endif
ifneq ($(XTENSA), 0) ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1 $(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
@@ -738,7 +729,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo 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=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -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 GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
ifneq ($(OS),Windows_NT) ifneq ($(OS),Windows_NT)
@@ -790,7 +780,6 @@ endif
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
+2 -6
View File
@@ -43,13 +43,12 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux. You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 94 microcontroller boards are currently supported: The following 91 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) * [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
* [Adafruit CLUE](https://www.adafruit.com/product/4500) * [Adafruit CLUE](https://www.adafruit.com/product/4500)
* [Adafruit Feather M0](https://www.adafruit.com/product/2772) * [Adafruit Feather M0](https://www.adafruit.com/product/2772)
* [Adafruit Feather M0 Express](https://www.adafruit.com/product/3403)
* [Adafruit Feather M4](https://www.adafruit.com/product/3857) * [Adafruit Feather M4](https://www.adafruit.com/product/3857)
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759) * [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062) * [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
@@ -60,7 +59,6 @@ The following 94 microcontroller boards are currently supported:
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727) * [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800) * [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481) * [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
* [Adafruit KB2040](https://www.adafruit.com/product/5302)
* [Adafruit MacroPad RP2040](https://www.adafruit.com/product/5100) * [Adafruit MacroPad RP2040](https://www.adafruit.com/product/5100)
* [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745) * [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745)
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000) * [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
@@ -138,10 +136,8 @@ The following 94 microcontroller boards are currently supported:
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill) * [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html) * [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
* [ST Micro STM32F469 "Discovery"](https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f469idiscovery.html) * [ST Micro STM32F469 "Discovery"](https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f469idiscovery.html)
* [The Things Industries Generic Node Sensor Edition](https://www.genericnode.com/docs/sensor-edition/)
* [Waveshare RP2040-Zero](https://www.waveshare.com/wiki/RP2040-Zero)
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/) * [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
* [The Things Industries Generic Node Sensor Edition](https://www.genericnode.com/docs/sensor-edition/)
For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome! For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome!
+116 -112
View File
@@ -42,8 +42,8 @@ type BuildResult struct {
// information. Used for GDB for example. // information. Used for GDB for example.
Executable string Executable string
// A path to the output binary. It is stored in the tmpdir directory of the // A path to the output binary. It will be removed after Build returns, so
// Build function, so if it should be kept it must be copied or moved away. // if it should be kept it must be copied or moved away.
// It is often the same as Executable, but differs if the output format is // It is often the same as Executable, but differs if the output format is
// .hex for example (instead of the usual ELF). // .hex for example (instead of the usual ELF).
Binary string Binary string
@@ -94,16 +94,23 @@ type packageAction struct {
// //
// The error value may be of type *MultiError. Callers will likely want to check // The error value may be of type *MultiError. Callers will likely want to check
// for this case and print such errors individually. // for this case and print such errors individually.
func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildResult, error) { func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
// Read the build ID of the tinygo binary. // Read the build ID of the tinygo binary.
// Used as a cache key for package builds. // Used as a cache key for package builds.
compilerBuildID, err := ReadBuildID() compilerBuildID, err := ReadBuildID()
if err != nil { if err != nil {
return BuildResult{}, err return err
} }
// Create a temporary directory for intermediary files.
dir, err := os.MkdirTemp("", "tinygo")
if err != nil {
return err
}
if config.Options.Work { if config.Options.Work {
fmt.Printf("WORK=%s\n", tmpdir) fmt.Printf("WORK=%s\n", dir)
} else {
defer os.RemoveAll(dir)
} }
// Look up the build cache directory, which is used to speed up incremental // Look up the build cache directory, which is used to speed up incremental
@@ -112,7 +119,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if cacheDir == "off" { if cacheDir == "off" {
// Use temporary build directory instead, effectively disabling the // Use temporary build directory instead, effectively disabling the
// build cache. // build cache.
cacheDir = tmpdir cacheDir = dir
} }
// Check for a libc dependency. // Check for a libc dependency.
@@ -122,40 +129,40 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
var libcDependencies []*compileJob var libcDependencies []*compileJob
switch config.Target.Libc { switch config.Target.Libc {
case "darwin-libSystem": case "darwin-libSystem":
job := makeDarwinLibSystemJob(config, tmpdir) job := makeDarwinLibSystemJob(config, dir)
libcDependencies = append(libcDependencies, job) libcDependencies = append(libcDependencies, job)
case "musl": case "musl":
job, unlock, err := Musl.load(config, tmpdir) job, unlock, err := Musl.load(config, dir)
if err != nil { if err != nil {
return BuildResult{}, err return err
} }
defer unlock() defer unlock()
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o"))) libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
libcDependencies = append(libcDependencies, job) libcDependencies = append(libcDependencies, job)
case "picolibc": case "picolibc":
libcJob, unlock, err := Picolibc.load(config, tmpdir) libcJob, unlock, err := Picolibc.load(config, dir)
if err != nil { if err != nil {
return BuildResult{}, err return err
} }
defer unlock() defer unlock()
libcDependencies = append(libcDependencies, libcJob) libcDependencies = append(libcDependencies, libcJob)
case "wasi-libc": case "wasi-libc":
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a") path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) { if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
return BuildResult{}, errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?") return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
} }
libcDependencies = append(libcDependencies, dummyCompileJob(path)) libcDependencies = append(libcDependencies, dummyCompileJob(path))
case "mingw-w64": case "mingw-w64":
_, unlock, err := MinGW.load(config, tmpdir) _, unlock, err := MinGW.load(config, dir)
if err != nil { if err != nil {
return BuildResult{}, err return err
} }
unlock() unlock()
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...) libcDependencies = append(libcDependencies, makeMinGWExtraLibs(dir)...)
case "": case "":
// no library specified, so nothing to do // no library specified, so nothing to do
default: default:
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc) return fmt.Errorf("unknown libc: %s", config.Target.Libc)
} }
optLevel, sizeLevel, _ := config.OptLevels() optLevel, sizeLevel, _ := config.OptLevels()
@@ -163,7 +170,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
Triple: config.Triple(), Triple: config.Triple(),
CPU: config.CPU(), CPU: config.CPU(),
Features: config.Features(), Features: config.Features(),
ABI: config.ABI(),
GOOS: config.GOOS(), GOOS: config.GOOS(),
GOARCH: config.GOARCH(), GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(), CodeModel: config.CodeModel(),
@@ -182,7 +188,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// address spaces, etc). // address spaces, etc).
machine, err := compiler.NewTargetMachine(compilerConfig) machine, err := compiler.NewTargetMachine(compilerConfig)
if err != nil { if err != nil {
return BuildResult{}, err return err
} }
defer machine.Dispose() defer machine.Dispose()
@@ -190,21 +196,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{ lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine), Sizes: compiler.Sizes(machine),
}) })
result := BuildResult{ if err != nil {
ModuleRoot: lprogram.MainPkg().Module.Dir, return err
MainDir: lprogram.MainPkg().Dir,
ImportPath: lprogram.MainPkg().ImportPath,
}
if result.ModuleRoot == "" {
// If there is no module root, just the regular root.
result.ModuleRoot = lprogram.MainPkg().Root
}
if err != nil { // failed to load AST
return result, err
} }
err = lprogram.Parse() err = lprogram.Parse()
if err != nil { if err != nil {
return result, err return err
} }
// Create the *ssa.Program. This does not yet build the entire SSA of the // Create the *ssa.Program. This does not yet build the entire SSA of the
@@ -273,7 +270,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
} }
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), tmpdir, compilerConfig) job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
return err return err
}, },
} }
@@ -287,7 +284,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
for _, imported := range pkg.Pkg.Imports() { for _, imported := range pkg.Pkg.Imports() {
job, ok := packageActionIDJobs[imported.Path()] job, ok := packageActionIDJobs[imported.Path()]
if !ok { if !ok {
return result, fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path()) return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
} }
importedPackages = append(importedPackages, job) importedPackages = append(importedPackages, job)
actionIDDependencies = append(actionIDDependencies, job) actionIDDependencies = append(actionIDDependencies, job)
@@ -373,7 +370,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Packages are compiled independently anyway. // Packages are compiled independently anyway.
for _, cgoHeader := range pkg.CGoHeaders { for _, cgoHeader := range pkg.CGoHeaders {
// Store the header text in a temporary file. // Store the header text in a temporary file.
f, err := os.CreateTemp(tmpdir, "cgosnippet-*.c") f, err := os.CreateTemp(dir, "cgosnippet-*.c")
if err != nil { if err != nil {
return err return err
} }
@@ -421,7 +418,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return errors.New("global not found: " + globalName) return errors.New("global not found: " + globalName)
} }
name := global.Name() name := global.Name()
newGlobal := llvm.AddGlobal(mod, global.GlobalValueType(), name+".tmp") newGlobal := llvm.AddGlobal(mod, global.Type().ElementType(), name+".tmp")
global.ReplaceAllUsesWith(newGlobal) global.ReplaceAllUsesWith(newGlobal)
global.EraseFromParentAsGlobal() global.EraseFromParentAsGlobal()
newGlobal.SetName(name) newGlobal.SetName(name)
@@ -526,7 +523,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if pkgInit.IsNil() { if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path()) panic("init not found for " + pkg.Pkg.Path())
} }
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "") irbuilder.CreateCall(pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
} }
irbuilder.CreateRetVoid() irbuilder.CreateRetVoid()
@@ -582,17 +579,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Run jobs to produce the LLVM module. // Run jobs to produce the LLVM module.
err := runJobs(programJob, config.Options.Semaphore) err := runJobs(programJob, config.Options.Semaphore)
if err != nil { if err != nil {
return result, err return err
} }
// Generate output. // Generate output.
switch outext { switch outext {
case ".o": case ".o":
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile) llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil { if err != nil {
return result, err return err
} }
defer llvmBuf.Dispose() defer llvmBuf.Dispose()
return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666) return os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc": case ".bc":
var buf llvm.MemoryBuffer var buf llvm.MemoryBuffer
if config.UseThinLTO() { if config.UseThinLTO() {
@@ -601,10 +598,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
buf = llvm.WriteBitcodeToMemoryBuffer(mod) buf = llvm.WriteBitcodeToMemoryBuffer(mod)
} }
defer buf.Dispose() defer buf.Dispose()
return result, os.WriteFile(outpath, buf.Bytes(), 0666) return os.WriteFile(outpath, buf.Bytes(), 0666)
case ".ll": case ".ll":
data := []byte(mod.String()) data := []byte(mod.String())
return result, os.WriteFile(outpath, data, 0666) return os.WriteFile(outpath, data, 0666)
default: default:
panic("unreachable") panic("unreachable")
} }
@@ -615,7 +612,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// run all jobs in parallel as far as possible. // run all jobs in parallel as far as possible.
// Add job to write the output object file. // Add job to write the output object file.
objfile := filepath.Join(tmpdir, "main.o") objfile := filepath.Join(dir, "main.o")
outputObjectFileJob := &compileJob{ outputObjectFileJob := &compileJob{
description: "generate output file", description: "generate output file",
dependencies: []*compileJob{programJob}, dependencies: []*compileJob{programJob},
@@ -638,19 +635,19 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Prepare link command. // Prepare link command.
linkerDependencies := []*compileJob{outputObjectFileJob} linkerDependencies := []*compileJob{outputObjectFileJob}
result.Executable = filepath.Join(tmpdir, "main") executable := filepath.Join(dir, "main")
if config.GOOS() == "windows" { if config.GOOS() == "windows" {
result.Executable += ".exe" executable += ".exe"
} }
result.Binary = result.Executable // final file tmppath := executable // final file
ldflags := append(config.LDFlags(), "-o", result.Executable) ldflags := append(config.LDFlags(), "-o", executable)
// Add compiler-rt dependency if needed. Usually this is a simple load from // Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache. // a cache.
if config.Target.RTLib == "compiler-rt" { if config.Target.RTLib == "compiler-rt" {
job, unlock, err := CompilerRT.load(config, tmpdir) job, unlock, err := CompilerRT.load(config, dir)
if err != nil { if err != nil {
return result, err return err
} }
defer unlock() defer unlock()
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
@@ -664,7 +661,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{ job := &compileJob{
description: "compile extra file " + path, description: "compile extra file " + path,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
job.result = result job.result = result
return err return err
}, },
@@ -682,7 +679,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{ job := &compileJob{
description: "compile CGo file " + abspath, description: "compile CGo file " + abspath,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
job.result = result job.result = result
return err return err
}, },
@@ -703,18 +700,21 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Add embedded files. // Add embedded files.
linkerDependencies = append(linkerDependencies, embedFileObjects...) linkerDependencies = append(linkerDependencies, embedFileObjects...)
// Determine whether the compilation configuration would result in debug
// (DWARF) information in the object files.
var hasDebug = true
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.
hasDebug = false
}
// Strip debug information with -no-debug. // Strip debug information with -no-debug.
if hasDebug && !config.Debug() { if !config.Debug() {
for _, tag := range config.BuildTags() {
if tag == "baremetal" {
// Don't use -no-debug on baremetal targets. It makes no sense:
// the debug information isn't flashed to the device anyway.
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
}
}
if config.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" { if config.Target.Linker == "wasm-ld" {
// Don't just strip debug information, also compress relocations // Don't just strip debug information, also compress relocations
// while we're at it. Relocations can only be compressed when debug // while we're at it. Relocations can only be compressed when debug
@@ -725,7 +725,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ldflags = append(ldflags, "--strip-debug") ldflags = append(ldflags, "--strip-debug")
} else { } else {
// Other linkers may have different flags. // Other linkers may have different flags.
return result, errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker) return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
} }
} }
@@ -741,6 +741,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
ldflags = append(ldflags, dependency.result) ldflags = append(ldflags, dependency.result)
} }
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(config.Target.Linker, ldflags...)
}
if config.UseThinLTO() { if config.UseThinLTO() {
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" { if config.GOOS() == "windows" {
@@ -772,12 +775,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
"-mllvm", "--rotation-max-header-size=0") "-mllvm", "--rotation-max-header-size=0")
} }
} }
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(config.Target.Linker, ldflags...)
}
err = link(config.Target.Linker, ldflags...) err = link(config.Target.Linker, ldflags...)
if err != nil { if err != nil {
return &commandError{"failed to link", result.Executable, err} return &commandError{"failed to link", executable, err}
} }
var calculatedStacks []string var calculatedStacks []string
@@ -786,7 +786,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Try to determine stack sizes at compile time. // Try to determine stack sizes at compile time.
// Don't do this by default as it usually doesn't work on // Don't do this by default as it usually doesn't work on
// unsupported architectures. // unsupported architectures.
calculatedStacks, stackSizes, err = determineStackSizes(mod, result.Executable) calculatedStacks, stackSizes, err = determineStackSizes(mod, executable)
if err != nil { if err != nil {
return err return err
} }
@@ -796,51 +796,41 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if config.AutomaticStackSize() { if config.AutomaticStackSize() {
// Modify the .tinygo_stacksizes section that contains a stack size // Modify the .tinygo_stacksizes section that contains a stack size
// for each goroutine. // for each goroutine.
err = modifyStackSizes(result.Executable, stackSizeLoads, stackSizes) err = modifyStackSizes(executable, stackSizeLoads, stackSizes)
if err != nil { if err != nil {
return fmt.Errorf("could not modify stack sizes: %w", err) return fmt.Errorf("could not modify stack sizes: %w", err)
} }
} }
if config.RP2040BootPatch() { if config.RP2040BootPatch() {
// Patch the second stage bootloader CRC into the .boot2 section // Patch the second stage bootloader CRC into the .boot2 section
err = patchRP2040BootCRC(result.Executable) err = patchRP2040BootCRC(executable)
if err != nil { if err != nil {
return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err) return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err)
} }
} }
// Run wasm-opt for wasm binaries // Run wasm-opt if necessary.
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" { if config.Scheduler() == "asyncify" {
var opt string var optLevel, shrinkLevel int
switch config.Options.Opt { switch config.Options.Opt {
case "none", "0": case "none", "0":
opt = "-O0"
case "1": case "1":
opt = "-O1" optLevel = 1
case "2": case "2":
opt = "-O2" optLevel = 2
case "s": case "s":
opt = "-Os" optLevel = 2
shrinkLevel = 1
case "z": case "z":
opt = "-Oz" optLevel = 2
shrinkLevel = 2
default: default:
return fmt.Errorf("unknown opt level: %q", config.Options.Opt) return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
} }
cmd := exec.Command(goenv.Get("WASMOPT"), "--asyncify", "-g",
var args []string "--optimize-level", strconv.Itoa(optLevel),
"--shrink-level", strconv.Itoa(shrinkLevel),
if config.Scheduler() == "asyncify" { executable, "--output", executable)
args = append(args, "--asyncify")
}
args = append(args,
opt,
"-g",
result.Executable,
"--output", result.Executable,
)
cmd := exec.Command(goenv.Get("WASMOPT"), args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -856,7 +846,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path() packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
} }
sizes, err := loadProgramSize(result.Executable, packagePathMap) sizes, err := loadProgramSize(executable, packagePathMap)
if err != nil { if err != nil {
return err return err
} }
@@ -892,7 +882,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// is simpler and cannot be parallelized. // is simpler and cannot be parallelized.
err = runJobs(linkJob, config.Options.Semaphore) err = runJobs(linkJob, config.Options.Semaphore)
if err != nil { if err != nil {
return result, err return err
} }
// Get an Intel .hex file or .bin file from the .elf file. // Get an Intel .hex file or .bin file from the .elf file.
@@ -903,43 +893,56 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
case "hex", "bin": case "hex", "bin":
// Extract raw binary, either encoding it as a hex file or as a raw // Extract raw binary, either encoding it as a hex file or as a raw
// firmware file. // firmware file.
result.Binary = filepath.Join(tmpdir, "main"+outext) tmppath = filepath.Join(dir, "main"+outext)
err := objcopy(result.Executable, result.Binary, outputBinaryFormat) err := objcopy(executable, tmppath, outputBinaryFormat)
if err != nil { if err != nil {
return result, err return err
} }
case "uf2": case "uf2":
// Get UF2 from the .elf file. // Get UF2 from the .elf file.
result.Binary = filepath.Join(tmpdir, "main"+outext) tmppath = filepath.Join(dir, "main"+outext)
err := convertELFFileToUF2File(result.Executable, result.Binary, config.Target.UF2FamilyID) err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
if err != nil { if err != nil {
return result, err return err
} }
case "esp32", "esp32-img", "esp32c3", "esp8266": case "esp32", "esp32-img", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM // Special format for the ESP family of chips (parsed by the ROM
// bootloader). // bootloader).
result.Binary = filepath.Join(tmpdir, "main"+outext) tmppath = filepath.Join(dir, "main"+outext)
err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat) err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
if err != nil { if err != nil {
return result, err return err
} }
case "nrf-dfu": case "nrf-dfu":
// special format for nrfutil for Nordic chips // special format for nrfutil for Nordic chips
tmphexpath := filepath.Join(tmpdir, "main.hex") tmphexpath := filepath.Join(dir, "main.hex")
err := objcopy(result.Executable, tmphexpath, "hex") err := objcopy(executable, tmphexpath, "hex")
if err != nil { if err != nil {
return result, err return err
} }
result.Binary = filepath.Join(tmpdir, "main"+outext) tmppath = filepath.Join(dir, "main"+outext)
err = makeDFUFirmwareImage(config.Options, tmphexpath, result.Binary) err = makeDFUFirmwareImage(config.Options, tmphexpath, tmppath)
if err != nil { if err != nil {
return result, err return err
} }
default: default:
return result, fmt.Errorf("unknown output binary format: %s", outputBinaryFormat) return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
} }
return result, nil // If there's a module root, use that.
moduleroot := lprogram.MainPkg().Module.Dir
if moduleroot == "" {
// if not, just the regular root
moduleroot = lprogram.MainPkg().Root
}
return action(BuildResult{
Executable: executable,
Binary: tmppath,
MainDir: lprogram.MainPkg().Dir,
ModuleRoot: moduleroot,
ImportPath: lprogram.MainPkg().ImportPath,
})
} }
// createEmbedObjectFile creates a new object file with the given contents, for // createEmbedObjectFile creates a new object file with the given contents, for
@@ -1083,6 +1086,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
// cannot be represented exactly in JavaScript (JS only has doubles). To // cannot be represented exactly in JavaScript (JS only has doubles). To
// keep functions interoperable, pass int64 types as pointers to // keep functions interoperable, pass int64 types as pointers to
// stack-allocated values. // stack-allocated values.
// Use -wasm-abi=generic to disable this behaviour.
if config.WasmAbi() == "js" { if config.WasmAbi() == "js" {
err := transform.ExternalInt64AsPtr(mod, config) err := transform.ExternalInt64AsPtr(mod, config)
if err != nil { if err != nil {
@@ -1133,7 +1137,7 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
// A strin is a {ptr, len} pair. We need these types to build the // A strin is a {ptr, len} pair. We need these types to build the
// initializer. // initializer.
initializerType := global.GlobalValueType() initializerType := global.Type().ElementType()
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" { if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
return fmt.Errorf("%s: not a string", globalName) return fmt.Errorf("%s: not a string", globalName)
} }
@@ -1152,7 +1156,7 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
// Create the string value, which is a {ptr, len} pair. // Create the string value, which is a {ptr, len} pair.
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false) zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
ptr := llvm.ConstGEP(bufInitializer.Type(), buf, []llvm.Value{zero, zero}) ptr := llvm.ConstGEP(buf, []llvm.Value{zero, zero})
if ptr.Type() != elementTypes[0] { if ptr.Type() != elementTypes[0] {
return fmt.Errorf("%s: not a string", globalName) return fmt.Errorf("%s: not a string", globalName)
} }
-1
View File
@@ -59,7 +59,6 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "darwin", GOARCH: "amd64"}, {GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"}, {GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"}, {GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"},
} { } {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" { if options.GOARCH == "arm" {
+2 -14
View File
@@ -162,15 +162,6 @@ var aeabiBuiltins = []string{
"udivmodsi4.c", "udivmodsi4.c",
} }
var avrBuiltins = []string{
"avr/divmodhi4.S",
"avr/divmodqi4.S",
"avr/mulhi3.S",
"avr/mulqi3.S",
"avr/udivmodhi4.S",
"avr/udivmodqi4.S",
}
// CompilerRT is a library with symbols required by programs compiled with LLVM. // CompilerRT is a library with symbols required by programs compiled with LLVM.
// These symbols are for operations that cannot be emitted with a single // These symbols are for operations that cannot be emitted with a single
// instruction or a short sequence of instructions for that target. // instruction or a short sequence of instructions for that target.
@@ -190,14 +181,11 @@ var CompilerRT = Library{
// Development build. // Development build.
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins") return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
}, },
librarySources: func(target string) ([]string, error) { librarySources: func(target string) []string {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
builtins = append(builtins, aeabiBuiltins...) builtins = append(builtins, aeabiBuiltins...)
} }
if strings.HasPrefix(target, "avr") { return builtins
builtins = append(builtins, avrBuiltins...)
}
return builtins, nil
}, },
} }
+4 -19
View File
@@ -1,4 +1,4 @@
//go:build byollvm // +build byollvm
//===-- cc1as.cpp - Clang Assembler --------------------------------------===// //===-- cc1as.cpp - Clang Assembler --------------------------------------===//
// //
@@ -101,9 +101,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
// Target Options // Target Options
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu)); Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
Opts.Features = Args.getAllArgValues(OPT_target_feature); Opts.Features = Args.getAllArgValues(OPT_target_feature);
@@ -206,14 +203,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
.Default(0); .Default(0);
} }
if (auto *A = Args.getLastArg(OPT_femit_dwarf_unwind_EQ)) {
Opts.EmitDwarfUnwind =
llvm::StringSwitch<EmitDwarfUnwindType>(A->getValue())
.Case("always", EmitDwarfUnwindType::Always)
.Case("no-compact-unwind", EmitDwarfUnwindType::NoCompactUnwind)
.Case("default", EmitDwarfUnwindType::Default);
}
return Success; return Success;
} }
@@ -264,8 +253,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
assert(MRI && "Unable to create target register info!"); assert(MRI && "Unable to create target register info!");
MCTargetOptions MCOptions; MCTargetOptions MCOptions;
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
std::unique_ptr<MCAsmInfo> MAI( std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions)); TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
assert(MAI && "Unable to create target asm info!"); assert(MAI && "Unable to create target asm info!");
@@ -312,8 +299,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
// MCObjectFileInfo needs a MCContext reference in order to initialize itself. // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
std::unique_ptr<MCObjectFileInfo> MOFI( std::unique_ptr<MCObjectFileInfo> MOFI(
TheTarget->createMCObjectFileInfo(Ctx, PIC)); TheTarget->createMCObjectFileInfo(Ctx, PIC));
if (Opts.DarwinTargetVariantTriple)
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
Ctx.setObjectFileInfo(MOFI.get()); Ctx.setObjectFileInfo(MOFI.get());
if (Opts.SaveTemporaryLabels) if (Opts.SaveTemporaryLabels)
@@ -362,7 +347,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
std::unique_ptr<MCCodeEmitter> CE; std::unique_ptr<MCCodeEmitter> CE;
if (Opts.ShowEncoding) if (Opts.ShowEncoding)
CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx)); CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
std::unique_ptr<MCAsmBackend> MAB( std::unique_ptr<MCAsmBackend> MAB(
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
@@ -382,7 +367,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
} }
std::unique_ptr<MCCodeEmitter> CE( std::unique_ptr<MCCodeEmitter> CE(
TheTarget->createMCCodeEmitter(*MCII, Ctx)); TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
std::unique_ptr<MCAsmBackend> MAB( std::unique_ptr<MCAsmBackend> MAB(
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
assert(MAB && "Unable to create asm backend!"); assert(MAB && "Unable to create asm backend!");
@@ -404,7 +389,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) { if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) {
MCSection *AsmLabel = Ctx.getMachOSection( MCSection *AsmLabel = Ctx.getMachOSection(
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly()); "__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
Str.get()->switchSection(AsmLabel); Str.get()->SwitchSection(AsmLabel);
Str.get()->emitZeros(1); Str.get()->emitZeros(1);
} }
-7
View File
@@ -85,9 +85,6 @@ struct AssemblerInvocation {
unsigned IncrementalLinkerCompatible : 1; unsigned IncrementalLinkerCompatible : 1;
unsigned EmbedBitcode : 1; unsigned EmbedBitcode : 1;
/// Whether to emit DWARF unwind info.
EmitDwarfUnwindType EmitDwarfUnwind;
/// The name of the relocation model to use. /// The name of the relocation model to use.
std::string RelocationModel; std::string RelocationModel;
@@ -95,9 +92,6 @@ struct AssemblerInvocation {
/// otherwise. /// otherwise.
std::string TargetABI; std::string TargetABI;
/// Darwin target variant triple, the variant of the deployment target
/// for which the code is being compiled.
llvm::Optional<llvm::Triple> DarwinTargetVariantTriple;
/// @} /// @}
public: public:
@@ -118,7 +112,6 @@ public:
Dwarf64 = 0; Dwarf64 = 0;
DwarfVersion = 0; DwarfVersion = 0;
EmbedBitcode = 0; EmbedBitcode = 0;
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
} }
static bool CreateFromArgs(AssemblerInvocation &Res, static bool CreateFromArgs(AssemblerInvocation &Res,
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build byollvm // +build byollvm
#include <clang/Basic/DiagnosticOptions.h> #include <clang/Basic/DiagnosticOptions.h>
#include <clang/CodeGen/CodeGenAction.h> #include <clang/CodeGen/CodeGenAction.h>
+2 -2
View File
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err) return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
} }
if major != 1 || minor < 18 || minor > 20 { if major != 1 || minor < 18 || minor > 19 {
return nil, fmt.Errorf("requires go version 1.18 through 1.20, got go%d.%d", major, minor) return nil, fmt.Errorf("requires go version 1.18 through 1.19, got go%d.%d", major, minor)
} }
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
+5 -18
View File
@@ -29,7 +29,7 @@ type Library struct {
sourceDir func() string sourceDir func() string
// The source files, relative to sourceDir. // The source files, relative to sourceDir.
librarySources func(target string) ([]string, error) librarySources func(target string) []string
// The source code for the crt1.o file, relative to sourceDir. // The source code for the crt1.o file, relative to sourceDir.
crt1Source string crt1Source string
@@ -142,7 +142,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// Note: -fdebug-prefix-map is necessary to make the output archive // Note: -fdebug-prefix-map is necessary to make the output archive
// reproducible. Otherwise the temporary directory is stored in the archive // reproducible. Otherwise the temporary directory is stored in the archive
// itself, which varies each run. // itself, which varies each run.
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir) args := append(l.cflags(target, headerPath), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
cpu := config.CPU() cpu := config.CPU()
if cpu != "" { if cpu != "" {
// X86 has deprecated the -mcpu flag, so we need to use -march instead. // X86 has deprecated the -mcpu flag, so we need to use -march instead.
@@ -155,9 +155,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
args = append(args, "-mcpu="+cpu) args = append(args, "-mcpu="+cpu)
} }
} }
if config.ABI() != "" {
args = append(args, "-mabi="+config.ABI())
}
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
if strings.Split(target, "-")[2] == "linux" { if strings.Split(target, "-")[2] == "linux" {
args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables") args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
@@ -173,10 +170,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
args = append(args, "-mdouble=64") args = append(args, "-mdouble=64")
} }
if strings.HasPrefix(target, "riscv32-") { if strings.HasPrefix(target, "riscv32-") {
args = append(args, "-march=rv32imac", "-fforce-enable-int128") args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
} }
if strings.HasPrefix(target, "riscv64-") { if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc") args = append(args, "-march=rv64gc", "-mabi=lp64")
} }
if strings.HasPrefix(target, "xtensa") { if strings.HasPrefix(target, "xtensa") {
// Hack to work around an issue in the Xtensa port: // Hack to work around an issue in the Xtensa port:
@@ -222,11 +219,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// Create jobs to compile all sources. These jobs are depended upon by the // Create jobs to compile all sources. These jobs are depended upon by the
// archive job above, so must be run first. // archive job above, so must be run first.
paths, err := l.librarySources(target) for _, path := range l.librarySources(target) {
if err != nil {
return nil, nil, err
}
for _, path := range paths {
// Strip leading "../" parts off the path. // Strip leading "../" parts off the path.
cleanpath := path cleanpath := path
for strings.HasPrefix(cleanpath, "../") { for strings.HasPrefix(cleanpath, "../") {
@@ -242,9 +235,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
var compileArgs []string var compileArgs []string
compileArgs = append(compileArgs, args...) compileArgs = append(compileArgs, args...)
compileArgs = append(compileArgs, "-o", objpath, srcpath) compileArgs = append(compileArgs, "-o", objpath, srcpath)
if config.Options.PrintCommands != nil {
config.Options.PrintCommands("clang", compileArgs...)
}
err := runCCompiler(compileArgs...) err := runCCompiler(compileArgs...)
if err != nil { if err != nil {
return &commandError{"failed to build", srcpath, err} return &commandError{"failed to build", srcpath, err}
@@ -271,9 +261,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
} }
tmpfile.Close() tmpfile.Close()
compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath) compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath)
if config.Options.PrintCommands != nil {
config.Options.PrintCommands("clang", compileArgs...)
}
err = runCCompiler(compileArgs...) err = runCCompiler(compileArgs...)
if err != nil { if err != nil {
return &commandError{"failed to build", srcpath, err} return &commandError{"failed to build", srcpath, err}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build byollvm // +build byollvm
// This file provides C wrappers for liblld. // This file provides C wrappers for liblld.
+6 -18
View File
@@ -1,7 +1,6 @@
package builder package builder
import ( import (
"fmt"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
@@ -32,9 +31,9 @@ var MinGW = Library{
// No flags necessary because there are no files to compile. // No flags necessary because there are no files to compile.
return nil return nil
}, },
librarySources: func(target string) ([]string, error) { librarySources: func(target string) []string {
// We only use the UCRT DLL file. No source files necessary. // We only use the UCRT DLL file. No source files necessary.
return nil, nil return nil
}, },
} }
@@ -44,7 +43,7 @@ var MinGW = Library{
// //
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to // TODO: cache the result. At the moment, it costs a few hundred milliseconds to
// compile these files. // compile these files.
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob { func makeMinGWExtraLibs(tmpdir string) []*compileJob {
var jobs []*compileJob var jobs []*compileJob
root := goenv.Get("TINYGOROOT") root := goenv.Get("TINYGOROOT")
// Normally all the api-ms-win-crt-*.def files are all compiled to a single // Normally all the api-ms-win-crt-*.def files are all compiled to a single
@@ -53,7 +52,7 @@ func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
for _, name := range []string{ for _, name := range []string{
"kernel32.def.in", "kernel32.def.in",
"api-ms-win-crt-conio-l1-1-0.def", "api-ms-win-crt-conio-l1-1-0.def",
"api-ms-win-crt-convert-l1-1-0.def.in", "api-ms-win-crt-convert-l1-1-0.def",
"api-ms-win-crt-environment-l1-1-0.def", "api-ms-win-crt-environment-l1-1-0.def",
"api-ms-win-crt-filesystem-l1-1-0.def", "api-ms-win-crt-filesystem-l1-1-0.def",
"api-ms-win-crt-heap-l1-1-0.def", "api-ms-win-crt-heap-l1-1-0.def",
@@ -75,27 +74,16 @@ func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
result: outpath, result: outpath,
run: func(job *compileJob) error { run: func(job *compileJob) error {
defpath := inpath defpath := inpath
var archDef, emulation string
switch goarch {
case "amd64":
archDef = "-DDEF_X64"
emulation = "i386pep"
case "arm64":
archDef = "-DDEF_ARM64"
emulation = "arm64pe"
default:
return fmt.Errorf("unsupported architecture for mingw-w64: %s", goarch)
}
if strings.HasSuffix(inpath, ".in") { if strings.HasSuffix(inpath, ".in") {
// .in files need to be preprocessed by a preprocessor (-E) // .in files need to be preprocessed by a preprocessor (-E)
// first. // first.
defpath = outpath + ".def" defpath = outpath + ".def"
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", archDef, "-DDATA", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/") err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-DDATA", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
if err != nil { if err != nil {
return err return err
} }
} }
return link("ld.lld", "-m", emulation, "-o", outpath, defpath) return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath)
}, },
} }
jobs = append(jobs, job) jobs = append(jobs, job)
+6 -22
View File
@@ -6,12 +6,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
) )
var Musl = Library{ var Musl = Library{
@@ -79,7 +77,7 @@ var Musl = Library{
cflags: func(target, headerPath string) []string { cflags: func(target, headerPath string) []string {
arch := compileopts.MuslArchitecture(target) arch := compileopts.MuslArchitecture(target)
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl") muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl")
cflags := []string{ return []string{
"-std=c99", // same as in musl "-std=c99", // same as in musl
"-D_XOPEN_SOURCE=700", // same as in musl "-D_XOPEN_SOURCE=700", // same as in musl
// Musl triggers some warnings and we don't want to show any // Musl triggers some warnings and we don't want to show any
@@ -92,7 +90,6 @@ var Musl = Library{
"-Wno-ignored-attributes", "-Wno-ignored-attributes",
"-Wno-string-plus-int", "-Wno-string-plus-int",
"-Wno-ignored-pragmas", "-Wno-ignored-pragmas",
"-Wno-tautological-constant-out-of-range-compare",
"-Qunused-arguments", "-Qunused-arguments",
// Select include dirs. Don't include standard library includes // Select include dirs. Don't include standard library includes
// (that would introduce host dependencies and other complications), // (that would introduce host dependencies and other complications),
@@ -106,15 +103,9 @@ var Musl = Library{
"-I" + muslDir + "/include", "-I" + muslDir + "/include",
"-fno-stack-protector", "-fno-stack-protector",
} }
llvmMajor, _ := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if llvmMajor >= 15 {
// This flag was added in Clang 15. It is not present in LLVM 14.
cflags = append(cflags, "-Wno-deprecated-non-prototype")
}
return cflags
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
librarySources: func(target string) ([]string, error) { librarySources: func(target string) []string {
arch := compileopts.MuslArchitecture(target) arch := compileopts.MuslArchitecture(target)
globs := []string{ globs := []string{
"env/*.c", "env/*.c",
@@ -126,21 +117,17 @@ var Musl = Library{
"internal/vdso.c", "internal/vdso.c",
"legacy/*.c", "legacy/*.c",
"malloc/*.c", "malloc/*.c",
"malloc/mallocng/*.c",
"mman/*.c", "mman/*.c",
"math/*.c", "math/*.c",
"signal/*.c", "signal/*.c",
"stdio/*.c", "stdio/*.c",
"string/*.c", "string/*.c",
"thread/" + arch + "/*.s", "thread/" + arch + "/*.s",
"thread/" + arch + "/*.c",
"thread/*.c", "thread/*.c",
"time/*.c", "time/*.c",
"unistd/*.c", "unistd/*.c",
} }
if arch == "arm" {
// These files need to be added to the start for some reason.
globs = append([]string{"thread/arm/*.c"}, globs...)
}
var sources []string var sources []string
seenSources := map[string]struct{}{} seenSources := map[string]struct{}{}
@@ -154,16 +141,13 @@ var Musl = Library{
// > ErrBadPattern, when pattern is malformed. // > ErrBadPattern, when pattern is malformed.
// So the only possible error is when the (statically defined) // So the only possible error is when the (statically defined)
// pattern is wrong. In other words, a programming bug. // pattern is wrong. In other words, a programming bug.
return nil, fmt.Errorf("musl: could not glob source dirs: %w", err) panic("could not glob source dirs: " + err.Error())
}
if len(matches) == 0 {
return nil, fmt.Errorf("musl: did not find any files for pattern %#v", pattern)
} }
for _, match := range matches { for _, match := range matches {
relpath, err := filepath.Rel(basepath, match) relpath, err := filepath.Rel(basepath, match)
if err != nil { if err != nil {
// Not sure if this is even possible. // Not sure if this is even possible.
return nil, err panic(err)
} }
// Make sure architecture specific files override generic files. // Make sure architecture specific files override generic files.
id := strings.ReplaceAll(relpath, "/"+arch+"/", "/") id := strings.ReplaceAll(relpath, "/"+arch+"/", "/")
@@ -175,7 +159,7 @@ var Musl = Library{
sources = append(sources, relpath) sources = append(sources, relpath)
} }
} }
return sources, nil return sources
}, },
crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c
} }
+102 -103
View File
@@ -25,13 +25,11 @@ var Picolibc = Library{
"-Wall", "-Wall",
"-std=gnu11", "-std=gnu11",
"-D_COMPILING_NEWLIB", "-D_COMPILING_NEWLIB",
"-D_HAVE_ALIAS_ATTRIBUTE", "-DHAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO", "-DTINY_STDIO",
"-DPOSIX_IO",
"-D_IEEE_LIBM", "-D_IEEE_LIBM",
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU "-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
"-D__OBSOLETE_MATH_DOUBLE=0", "-D__OBSOLETE_MATH_DOUBLE=0",
"-D_WANT_IO_C99_FORMATS",
"-nostdlibinc", "-nostdlibinc",
"-isystem", newlibDir + "/libc/include", "-isystem", newlibDir + "/libc/include",
"-I" + newlibDir + "/libc/tinystdio", "-I" + newlibDir + "/libc/tinystdio",
@@ -40,30 +38,39 @@ var Picolibc = Library{
} }
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
librarySources: func(target string) ([]string, error) { librarySources: func(target string) []string {
return picolibcSources, nil return picolibcSources
}, },
} }
var picolibcSources = []string{ var picolibcSources = []string{
"../../picolibc-stdio.c", "../../picolibc-stdio.c",
// srcs_tinystdio
"libc/tinystdio/asprintf.c", "libc/tinystdio/asprintf.c",
"libc/tinystdio/bufio.c", "libc/tinystdio/atod_engine.c",
"libc/tinystdio/atod_ryu.c",
"libc/tinystdio/atof_engine.c",
"libc/tinystdio/atof_ryu.c",
//"libc/tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
"libc/tinystdio/clearerr.c", "libc/tinystdio/clearerr.c",
"libc/tinystdio/ecvt_r.c", "libc/tinystdio/compare_exchange.c",
"libc/tinystdio/dtoa_data.c",
"libc/tinystdio/dtoa_engine.c",
"libc/tinystdio/dtoa_ryu.c",
"libc/tinystdio/ecvtbuf.c",
"libc/tinystdio/ecvt.c", "libc/tinystdio/ecvt.c",
"libc/tinystdio/ecvtf_r.c", "libc/tinystdio/ecvt_data.c",
"libc/tinystdio/ecvtfbuf.c",
"libc/tinystdio/ecvtf.c", "libc/tinystdio/ecvtf.c",
"libc/tinystdio/ecvtf_data.c",
"libc/tinystdio/exchange.c",
//"libc/tinystdio/fclose.c", // posix-io
"libc/tinystdio/fcvtbuf.c",
"libc/tinystdio/fcvt.c", "libc/tinystdio/fcvt.c",
"libc/tinystdio/fcvt_r.c", "libc/tinystdio/fcvtfbuf.c",
"libc/tinystdio/fcvtf.c", "libc/tinystdio/fcvtf.c",
"libc/tinystdio/fcvtf_r.c",
"libc/tinystdio/gcvt.c",
"libc/tinystdio/gcvtf.c",
"libc/tinystdio/fclose.c",
"libc/tinystdio/fdevopen.c", "libc/tinystdio/fdevopen.c",
//"libc/tinystdio/fdopen.c", // posix-io
"libc/tinystdio/feof.c", "libc/tinystdio/feof.c",
"libc/tinystdio/ferror.c", "libc/tinystdio/ferror.c",
"libc/tinystdio/fflush.c", "libc/tinystdio/fflush.c",
@@ -71,60 +78,70 @@ var picolibcSources = []string{
"libc/tinystdio/fgets.c", "libc/tinystdio/fgets.c",
"libc/tinystdio/fileno.c", "libc/tinystdio/fileno.c",
"libc/tinystdio/filestrget.c", "libc/tinystdio/filestrget.c",
"libc/tinystdio/filestrput.c",
"libc/tinystdio/filestrputalloc.c", "libc/tinystdio/filestrputalloc.c",
"libc/tinystdio/fmemopen.c", "libc/tinystdio/filestrput.c",
//"libc/tinystdio/fopen.c", // posix-io
"libc/tinystdio/fprintf.c", "libc/tinystdio/fprintf.c",
"libc/tinystdio/fputc.c", "libc/tinystdio/fputc.c",
"libc/tinystdio/fputs.c", "libc/tinystdio/fputs.c",
"libc/tinystdio/fread.c", "libc/tinystdio/fread.c",
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
"libc/tinystdio/fscanf.c", "libc/tinystdio/fscanf.c",
"libc/tinystdio/fseek.c", "libc/tinystdio/fseek.c",
"libc/tinystdio/fseeko.c",
"libc/tinystdio/ftell.c", "libc/tinystdio/ftell.c",
"libc/tinystdio/ftello.c", "libc/tinystdio/ftoa_data.c",
"libc/tinystdio/ftoa_engine.c",
"libc/tinystdio/ftoa_ryu.c",
"libc/tinystdio/fwrite.c", "libc/tinystdio/fwrite.c",
"libc/tinystdio/gcvtbuf.c",
"libc/tinystdio/gcvt.c",
"libc/tinystdio/gcvtfbuf.c",
"libc/tinystdio/gcvtf.c",
"libc/tinystdio/getchar.c", "libc/tinystdio/getchar.c",
"libc/tinystdio/gets.c", "libc/tinystdio/gets.c",
"libc/tinystdio/matchcaseprefix.c", "libc/tinystdio/matchcaseprefix.c",
"libc/tinystdio/mktemp.c",
"libc/tinystdio/perror.c", "libc/tinystdio/perror.c",
//"libc/tinystdio/posixiob.c", // posix-io
//"libc/tinystdio/posixio.c", // posix-io
"libc/tinystdio/printf.c", "libc/tinystdio/printf.c",
"libc/tinystdio/putchar.c", "libc/tinystdio/putchar.c",
"libc/tinystdio/puts.c", "libc/tinystdio/puts.c",
"libc/tinystdio/rewind.c", "libc/tinystdio/ryu_divpow2.c",
"libc/tinystdio/ryu_log10.c",
"libc/tinystdio/ryu_log2pow5.c",
"libc/tinystdio/ryu_pow5bits.c",
"libc/tinystdio/ryu_table.c",
"libc/tinystdio/ryu_umul128.c",
"libc/tinystdio/scanf.c", "libc/tinystdio/scanf.c",
"libc/tinystdio/setbuf.c", "libc/tinystdio/setbuf.c",
"libc/tinystdio/setbuffer.c",
"libc/tinystdio/setlinebuf.c",
"libc/tinystdio/setvbuf.c", "libc/tinystdio/setvbuf.c",
//"libc/tinystdio/sflags.c", // posix-io
"libc/tinystdio/snprintf.c", "libc/tinystdio/snprintf.c",
"libc/tinystdio/sprintf.c",
"libc/tinystdio/snprintfd.c", "libc/tinystdio/snprintfd.c",
"libc/tinystdio/snprintff.c", "libc/tinystdio/snprintff.c",
"libc/tinystdio/sprintff.c", "libc/tinystdio/sprintf.c",
"libc/tinystdio/sprintfd.c", "libc/tinystdio/sprintfd.c",
"libc/tinystdio/sprintff.c",
"libc/tinystdio/sscanf.c", "libc/tinystdio/sscanf.c",
"libc/tinystdio/strfromf.c",
"libc/tinystdio/strfromd.c", "libc/tinystdio/strfromd.c",
"libc/tinystdio/strtof.c", "libc/tinystdio/strfromf.c",
"libc/tinystdio/strtof_l.c",
"libc/tinystdio/strtod.c", "libc/tinystdio/strtod.c",
"libc/tinystdio/strtod_l.c", "libc/tinystdio/strtod_l.c",
"libc/tinystdio/strtof.c",
//"libc/tinystdio/strtold.c", // have_long_double and not long_double_equals_double
//"libc/tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
"libc/tinystdio/ungetc.c", "libc/tinystdio/ungetc.c",
"libc/tinystdio/vasprintf.c", "libc/tinystdio/vasprintf.c",
"libc/tinystdio/vfiprintf.c", "libc/tinystdio/vfiprintf.c",
"libc/tinystdio/vfiscanf.c",
"libc/tinystdio/vfprintf.c", "libc/tinystdio/vfprintf.c",
"libc/tinystdio/vfprintff.c", "libc/tinystdio/vfprintff.c",
"libc/tinystdio/vfscanf.c", "libc/tinystdio/vfscanf.c",
"libc/tinystdio/vfiscanf.c",
"libc/tinystdio/vfscanff.c", "libc/tinystdio/vfscanff.c",
"libc/tinystdio/vprintf.c", "libc/tinystdio/vprintf.c",
"libc/tinystdio/vscanf.c", "libc/tinystdio/vscanf.c",
"libc/tinystdio/vsscanf.c",
"libc/tinystdio/vsnprintf.c", "libc/tinystdio/vsnprintf.c",
"libc/tinystdio/vsprintf.c", "libc/tinystdio/vsprintf.c",
"libc/tinystdio/vsscanf.c",
"libc/string/bcmp.c", "libc/string/bcmp.c",
"libc/string/bcopy.c", "libc/string/bcopy.c",
@@ -283,7 +300,6 @@ var picolibcSources = []string{
"libm/common/s_expm1.c", "libm/common/s_expm1.c",
"libm/common/s_ilogb.c", "libm/common/s_ilogb.c",
"libm/common/s_infinity.c", "libm/common/s_infinity.c",
"libm/common/s_iseqsig.c",
"libm/common/s_isinf.c", "libm/common/s_isinf.c",
"libm/common/s_isinfd.c", "libm/common/s_isinfd.c",
"libm/common/s_isnan.c", "libm/common/s_isnan.c",
@@ -301,7 +317,6 @@ var picolibcSources = []string{
"libm/common/s_fmax.c", "libm/common/s_fmax.c",
"libm/common/s_fmin.c", "libm/common/s_fmin.c",
"libm/common/s_fpclassify.c", "libm/common/s_fpclassify.c",
"libm/common/s_getpayload.c",
"libm/common/s_lrint.c", "libm/common/s_lrint.c",
"libm/common/s_llrint.c", "libm/common/s_llrint.c",
"libm/common/s_lround.c", "libm/common/s_lround.c",
@@ -316,6 +331,7 @@ var picolibcSources = []string{
"libm/common/exp2.c", "libm/common/exp2.c",
"libm/common/exp_data.c", "libm/common/exp_data.c",
"libm/common/math_err_with_errno.c", "libm/common/math_err_with_errno.c",
"libm/common/math_err_xflow.c",
"libm/common/math_err_uflow.c", "libm/common/math_err_uflow.c",
"libm/common/math_err_oflow.c", "libm/common/math_err_oflow.c",
"libm/common/math_err_divzero.c", "libm/common/math_err_divzero.c",
@@ -323,8 +339,6 @@ var picolibcSources = []string{
"libm/common/math_err_may_uflow.c", "libm/common/math_err_may_uflow.c",
"libm/common/math_err_check_uflow.c", "libm/common/math_err_check_uflow.c",
"libm/common/math_err_check_oflow.c", "libm/common/math_err_check_oflow.c",
"libm/common/math_inexact.c",
"libm/common/math_inexactf.c",
"libm/common/log.c", "libm/common/log.c",
"libm/common/log_data.c", "libm/common/log_data.c",
"libm/common/log2.c", "libm/common/log2.c",
@@ -332,91 +346,76 @@ var picolibcSources = []string{
"libm/common/pow.c", "libm/common/pow.c",
"libm/common/pow_log_data.c", "libm/common/pow_log_data.c",
"libm/math/k_cos.c", "libm/math/e_acos.c",
"libm/math/k_rem_pio2.c", "libm/math/e_acosh.c",
"libm/math/k_sin.c", "libm/math/e_asin.c",
"libm/math/k_tan.c", "libm/math/e_atan2.c",
"libm/math/kf_cos.c", "libm/math/e_atanh.c",
"libm/math/kf_rem_pio2.c", "libm/math/e_cosh.c",
"libm/math/kf_sin.c", "libm/math/e_exp.c",
"libm/math/kf_tan.c", "libm/math/ef_acos.c",
"libm/math/s_acos.c", "libm/math/ef_acosh.c",
"libm/math/s_acosh.c", "libm/math/ef_asin.c",
"libm/math/s_asin.c", "libm/math/ef_atan2.c",
"libm/math/ef_atanh.c",
"libm/math/ef_cosh.c",
"libm/math/ef_exp.c",
"libm/math/ef_fmod.c",
"libm/math/ef_hypot.c",
"libm/math/ef_j0.c",
"libm/math/ef_j1.c",
"libm/math/ef_jn.c",
"libm/math/ef_lgamma.c",
"libm/math/ef_log10.c",
"libm/math/ef_log.c",
"libm/math/e_fmod.c",
"libm/math/ef_pow.c",
"libm/math/ef_remainder.c",
"libm/math/ef_rem_pio2.c",
"libm/math/ef_scalb.c",
"libm/math/ef_sinh.c",
"libm/math/ef_sqrt.c",
"libm/math/ef_tgamma.c",
"libm/math/e_hypot.c",
"libm/math/e_j0.c",
"libm/math/e_j1.c",
"libm/math/e_jn.c",
"libm/math/e_lgamma.c",
"libm/math/e_log10.c",
"libm/math/e_log.c",
"libm/math/e_pow.c",
"libm/math/e_remainder.c",
"libm/math/e_rem_pio2.c",
"libm/math/erf_lgamma.c",
"libm/math/er_lgamma.c",
"libm/math/e_scalb.c",
"libm/math/e_sinh.c",
"libm/math/e_sqrt.c",
"libm/math/e_tgamma.c",
"libm/math/s_asinh.c", "libm/math/s_asinh.c",
"libm/math/s_atan.c", "libm/math/s_atan.c",
"libm/math/s_atan2.c",
"libm/math/s_atanh.c",
"libm/math/s_ceil.c", "libm/math/s_ceil.c",
"libm/math/s_cos.c", "libm/math/s_cos.c",
"libm/math/s_cosh.c",
"libm/math/s_drem.c",
"libm/math/s_erf.c", "libm/math/s_erf.c",
"libm/math/s_exp.c",
"libm/math/s_exp2.c",
"libm/math/s_fabs.c", "libm/math/s_fabs.c",
"libm/math/s_floor.c",
"libm/math/s_fmod.c",
"libm/math/s_frexp.c",
"libm/math/s_gamma.c",
"libm/math/s_hypot.c",
"libm/math/s_j0.c",
"libm/math/s_j1.c",
"libm/math/s_jn.c",
"libm/math/s_lgamma.c",
"libm/math/s_log.c",
"libm/math/s_log10.c",
"libm/math/s_pow.c",
"libm/math/s_rem_pio2.c",
"libm/math/s_remainder.c",
"libm/math/s_scalb.c",
"libm/math/s_signif.c",
"libm/math/s_sin.c",
"libm/math/s_sincos.c",
"libm/math/s_sinh.c",
"libm/math/s_sqrt.c",
"libm/math/s_tan.c",
"libm/math/s_tanh.c",
"libm/math/s_tgamma.c",
"libm/math/sf_acos.c",
"libm/math/sf_acosh.c",
"libm/math/sf_asin.c",
"libm/math/sf_asinh.c", "libm/math/sf_asinh.c",
"libm/math/sf_atan.c", "libm/math/sf_atan.c",
"libm/math/sf_atan2.c",
"libm/math/sf_atanh.c",
"libm/math/sf_ceil.c", "libm/math/sf_ceil.c",
"libm/math/sf_cos.c", "libm/math/sf_cos.c",
"libm/math/sf_cosh.c",
"libm/math/sf_drem.c",
"libm/math/sf_erf.c", "libm/math/sf_erf.c",
"libm/math/sf_exp.c",
"libm/math/sf_exp2.c",
"libm/math/sf_fabs.c", "libm/math/sf_fabs.c",
"libm/math/sf_floor.c", "libm/math/sf_floor.c",
"libm/math/sf_fmod.c",
"libm/math/sf_frexp.c", "libm/math/sf_frexp.c",
"libm/math/sf_gamma.c", "libm/math/sf_ldexp.c",
"libm/math/sf_hypot.c", "libm/math/s_floor.c",
"libm/math/sf_j0.c", "libm/math/s_frexp.c",
"libm/math/sf_j1.c",
"libm/math/sf_jn.c",
"libm/math/sf_lgamma.c",
"libm/math/sf_log.c",
"libm/math/sf_log10.c",
"libm/math/sf_log2.c",
"libm/math/sf_pow.c",
"libm/math/sf_rem_pio2.c",
"libm/math/sf_remainder.c",
"libm/math/sf_scalb.c",
"libm/math/sf_signif.c", "libm/math/sf_signif.c",
"libm/math/sf_sin.c", "libm/math/sf_sin.c",
"libm/math/sf_sincos.c",
"libm/math/sf_sinh.c",
"libm/math/sf_sqrt.c",
"libm/math/sf_tan.c", "libm/math/sf_tan.c",
"libm/math/sf_tanh.c", "libm/math/sf_tanh.c",
"libm/math/sf_tgamma.c", "libm/math/s_ldexp.c",
"libm/math/sr_lgamma.c", "libm/math/s_signif.c",
"libm/math/srf_lgamma.c", "libm/math/s_sin.c",
"libm/math/s_tan.c",
"libm/math/s_tanh.c",
} }
+2 -1
View File
@@ -1,4 +1,5 @@
//go:build byollvm //go:build byollvm
// +build byollvm
package builder package builder
@@ -28,7 +29,7 @@ const hasBuiltinTools = true
func RunTool(tool string, args ...string) error { func RunTool(tool string, args ...string) error {
linker := "elf" linker := "elf"
if tool == "ld.lld" && len(args) >= 2 { if tool == "ld.lld" && len(args) >= 2 {
if args[0] == "-m" && (args[1] == "i386pep" || args[1] == "arm64pe") { if args[0] == "-m" && args[1] == "i386pep" {
linker = "mingw" linker = "mingw"
} else if args[0] == "-flavor" { } else if args[0] == "-flavor" {
linker = args[1] linker = args[1]
+1
View File
@@ -1,4 +1,5 @@
//go:build !byollvm //go:build !byollvm
// +build !byollvm
package builder package builder
-11
View File
@@ -86,8 +86,6 @@ var cgoAliases = map[string]string{
"C.uint32_t": "uint32", "C.uint32_t": "uint32",
"C.uint64_t": "uint64", "C.uint64_t": "uint64",
"C.uintptr_t": "uintptr", "C.uintptr_t": "uintptr",
"C.float": "float32",
"C.double": "float64",
} }
// builtinAliases are handled specially because they only exist on the Go side // builtinAliases are handled specially because they only exist on the Go side
@@ -311,12 +309,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// Process CGo imports for each file. // Process CGo imports for each file.
for i, f := range files { for i, f := range files {
cf := p.newCGoFile(f, i) cf := p.newCGoFile(f, i)
// Float and double are aliased, meaning that C.float is the same thing
// as float32 in Go.
cf.names["float"] = clangCursor{}
cf.names["double"] = clangCursor{}
// Now read all the names (identifies) that C defines in the header
// snippet.
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) { cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
for _, name := range builtinAliases { for _, name := range builtinAliases {
// Names such as C.int should not be obtained from C. // Names such as C.int should not be obtained from C.
@@ -956,9 +948,6 @@ func (p *cgoPackage) isEquivalentAST(a, b ast.Node) bool {
if !ok { if !ok {
return false return false
} }
if node == nil || b == nil {
return node == b
}
if len(node.List) != len(b.List) { if len(node.List) != len(b.List) {
return false return false
} }
+4 -89
View File
@@ -11,7 +11,6 @@ import (
"go/types" "go/types"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
@@ -22,15 +21,9 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
// normalizeResult normalizes Go source code that comes out of tests across // normalizeResult normalizes Go source code that comes out of tests across
// platforms and Go versions. // platforms and Go versions.
func normalizeResult(t *testing.T, result string) string { func normalizeResult(result string) string {
result = strings.ReplaceAll(result, "\r\n", "\n") actual := strings.ReplaceAll(result, "\r\n", "\n")
return actual
// This changed to 'undefined:', in Go 1.20.
result = strings.ReplaceAll(result, ": undeclared name:", ": undefined:")
// Go 1.20 added a bit more detail
result = regexp.MustCompile(`(unknown field z in struct literal).*`).ReplaceAllString(result, "$1")
return result
} }
func TestCGo(t *testing.T) { func TestCGo(t *testing.T) {
@@ -95,7 +88,7 @@ func TestCGo(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not write out CGo AST: %v", err) t.Errorf("could not write out CGo AST: %v", err)
} }
actual := normalizeResult(t, buf.String()) actual := normalizeResult(buf.String())
// Read the file with the expected output, to compare against. // Read the file with the expected output, to compare against.
outfile := filepath.Join("testdata", name+".out.go") outfile := filepath.Join("testdata", name+".out.go")
@@ -122,84 +115,6 @@ func TestCGo(t *testing.T) {
} }
} }
func Test_cgoPackage_isEquivalentAST(t *testing.T) {
fieldA := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "a"}}
fieldB := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "b"}}
listOfFieldA := &ast.FieldList{List: []*ast.Field{fieldA}}
listOfFieldB := &ast.FieldList{List: []*ast.Field{fieldB}}
funcDeclA := &ast.FuncDecl{Name: &ast.Ident{Name: "a"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldA}}
funcDeclB := &ast.FuncDecl{Name: &ast.Ident{Name: "b"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldB}}
funcDeclNoResults := &ast.FuncDecl{Name: &ast.Ident{Name: "C"}, Type: &ast.FuncType{Params: &ast.FieldList{}}}
testCases := []struct {
name string
a, b ast.Node
expected bool
}{
{
name: "both nil",
expected: true,
},
{
name: "not same type",
a: fieldA,
b: &ast.FuncDecl{},
expected: false,
},
{
name: "Field same",
a: fieldA,
b: fieldA,
expected: true,
},
{
name: "Field different",
a: fieldA,
b: fieldB,
expected: false,
},
{
name: "FuncDecl Type Results nil",
a: funcDeclNoResults,
b: funcDeclNoResults,
expected: true,
},
{
name: "FuncDecl Type Results same",
a: funcDeclA,
b: funcDeclA,
expected: true,
},
{
name: "FuncDecl Type Results different",
a: funcDeclA,
b: funcDeclB,
expected: false,
},
{
name: "FuncDecl Type Results a nil",
a: funcDeclNoResults,
b: funcDeclB,
expected: false,
},
{
name: "FuncDecl Type Results b nil",
a: funcDeclA,
b: funcDeclNoResults,
expected: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
p := &cgoPackage{}
if got := p.isEquivalentAST(tc.a, tc.b); tc.expected != got {
t.Errorf("expected %v, got %v", tc.expected, got)
}
})
}
}
// simpleImporter implements the types.Importer interface, but only allows // simpleImporter implements the types.Importer interface, but only allows
// importing the unsafe package. // importing the unsafe package.
type simpleImporter struct { type simpleImporter struct {
+2 -25
View File
@@ -14,9 +14,6 @@ import (
var ( var (
prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error) prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error)
precedences = map[token.Token]int{ precedences = map[token.Token]int{
token.OR: precedenceOr,
token.XOR: precedenceXor,
token.AND: precedenceAnd,
token.ADD: precedenceAdd, token.ADD: precedenceAdd,
token.SUB: precedenceAdd, token.SUB: precedenceAdd,
token.MUL: precedenceMul, token.MUL: precedenceMul,
@@ -27,9 +24,6 @@ var (
const ( const (
precedenceLowest = iota + 1 precedenceLowest = iota + 1
precedenceOr
precedenceXor
precedenceAnd
precedenceAdd precedenceAdd
precedenceMul precedenceMul
precedencePrefix precedencePrefix
@@ -82,7 +76,7 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
for t.peekToken != token.EOF && precedence < precedences[t.peekToken] { for t.peekToken != token.EOF && precedence < precedences[t.peekToken] {
switch t.peekToken { switch t.peekToken {
case token.OR, token.XOR, token.AND, token.ADD, token.SUB, token.MUL, token.QUO, token.REM: case token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
t.Next() t.Next()
leftExpr, err = parseBinaryExpr(t, leftExpr) leftExpr, err = parseBinaryExpr(t, leftExpr)
} }
@@ -205,18 +199,7 @@ func (t *tokenizer) Next() {
// https://en.cppreference.com/w/cpp/string/byte/isspace // https://en.cppreference.com/w/cpp/string/byte/isspace
t.peekPos++ t.peekPos++
t.buf = t.buf[1:] t.buf = t.buf[1:]
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&"): case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%':
// Two-character tokens.
switch c {
case '&':
t.peekToken = token.LAND
case '|':
t.peekToken = token.LOR
}
t.peekValue = t.buf[:2]
t.buf = t.buf[2:]
return
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
// Single-character tokens. // Single-character tokens.
// TODO: ++ (increment) and -- (decrement) operators. // TODO: ++ (increment) and -- (decrement) operators.
switch c { switch c {
@@ -234,12 +217,6 @@ func (t *tokenizer) Next() {
t.peekToken = token.QUO t.peekToken = token.QUO
case '%': case '%':
t.peekToken = token.REM t.peekToken = token.REM
case '&':
t.peekToken = token.AND
case '|':
t.peekToken = token.OR
case '^':
t.peekToken = token.XOR
} }
t.peekValue = t.buf[:1] t.peekValue = t.buf[:1]
t.buf = t.buf[1:] t.buf = t.buf[1:]
-4
View File
@@ -37,10 +37,6 @@ func TestParseConst(t *testing.T) {
{`5*5`, `5 * 5`}, {`5*5`, `5 * 5`},
{`5/5`, `5 / 5`}, {`5/5`, `5 / 5`},
{`5%5`, `5 % 5`}, {`5%5`, `5 % 5`},
{`5&5`, `5 & 5`},
{`5|5`, `5 | 5`},
{`5^5`, `5 ^ 5`},
{`5||5`, `error: 1:2: unexpected token ||, expected end of expression`}, // logical binops aren't supported yet
{`(5/5)`, `(5 / 5)`}, {`(5/5)`, `(5 / 5)`},
{`1 - 2`, `1 - 2`}, {`1 - 2`, `1 - 2`},
{`1 - 2 + 3`, `1 - 2 + 3`}, {`1 - 2 + 3`, `1 - 2 + 3`},
+15 -24
View File
@@ -533,26 +533,6 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
} }
// Get the precise location in the source code. Used for uniquely identifying
// source locations.
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) interface{} {
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),
}
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")
}
return location
}
// getCursorPosition returns a usable token.Pos from a libclang cursor. // getCursorPosition returns a usable token.Pos from a libclang cursor.
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos { func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor)) return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
@@ -808,7 +788,20 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
} }
if name == "" { if name == "" {
// Anonymous record, probably inside a typedef. // Anonymous record, probably inside a typedef.
location := f.getUniqueLocationID(pos, cursor) 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),
}
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) name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
} else { } else {
name = cgoRecordPrefix + name name = cgoRecordPrefix + name
@@ -821,9 +814,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
cursor := C.tinygo_clang_getTypeDeclaration(typ) cursor := C.tinygo_clang_getTypeDeclaration(typ)
name := getString(C.tinygo_clang_getCursorSpelling(cursor)) name := getString(C.tinygo_clang_getCursorSpelling(cursor))
if name == "" { if name == "" {
// Anonymous enum, probably inside a typedef. name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
location := f.getUniqueLocationID(pos, cursor)
name = f.getUnnamedDeclName("_Ctype_enum___", location)
} else { } else {
name = "enum_" + name name = "enum_" + name
} }
+2 -1
View File
@@ -1,4 +1,5 @@
//go:build !byollvm && llvm14 //go:build !byollvm
// +build !byollvm
package cgo package cgo
-15
View File
@@ -1,15 +0,0 @@
//go:build !byollvm && !llvm14
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-15/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@15/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
*/
import "C"
+2 -2
View File
@@ -8,9 +8,9 @@
// Type checking errors after CGo processing: // Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char 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:105: unknown field z in struct literal
// testdata/errors.go:108: undefined: C.SOME_CONST_1 // 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) // testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
// testdata/errors.go:112: undefined: C.SOME_CONST_4 // testdata/errors.go:112: undeclared name: C.SOME_CONST_4
package main package main
-4
View File
@@ -112,10 +112,6 @@ import "C"
import "C" import "C"
var ( var (
// aliases
_ C.float
_ C.double
// Simple typedefs. // Simple typedefs.
_ C.myint _ C.myint
+25 -25
View File
@@ -47,12 +47,6 @@ func (c *Config) Features() string {
return c.Target.Features + "," + c.Options.LLVMFeatures return c.Target.Features + "," + c.Options.LLVMFeatures
} }
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
// string is returned if the target doesn't specify an ABI.
func (c *Config) ABI() string {
return c.Target.ABI
}
// GOOS returns the GOOS of the target. This might not always be the actual OS: // GOOS returns the GOOS of the target. This might not always be the actual OS:
// for example, bare-metal targets will usually pretend to be linux to get the // for example, bare-metal targets will usually pretend to be linux to get the
// standard library to compile. // standard library to compile.
@@ -90,7 +84,7 @@ func (c *Config) CgoEnabled() bool {
} }
// GC returns the garbage collection strategy in use on this platform. Valid // GC returns the garbage collection strategy in use on this platform. Valid
// values are "none", "leaking", "conservative" and "precise". // values are "none", "leaking", and "conservative".
func (c *Config) GC() string { func (c *Config) GC() string {
if c.Options.GC != "" { if c.Options.GC != "" {
return c.Options.GC return c.Options.GC
@@ -105,7 +99,7 @@ func (c *Config) GC() string {
// that can be traced by the garbage collector. // that can be traced by the garbage collector.
func (c *Config) NeedsStackObjects() bool { func (c *Config) NeedsStackObjects() bool {
switch c.GC() { switch c.GC() {
case "conservative", "custom", "precise": case "conservative":
for _, tag := range c.BuildTags() { for _, tag := range c.BuildTags() {
if tag == "tinygo.wasm" { if tag == "tinygo.wasm" {
return true return true
@@ -190,11 +184,22 @@ func (c *Config) StackSize() uint64 {
return c.Target.DefaultStackSize return c.Target.DefaultStackSize
} }
// UseThinLTO returns whether ThinLTO should be used for the given target. // UseThinLTO returns whether ThinLTO should be used for the given target. Some
// targets (such as wasm) are not yet supported.
// We should try and remove as many exceptions as possible in the future, so
// that this optimization can be applied in more places.
func (c *Config) UseThinLTO() bool { func (c *Config) UseThinLTO() bool {
// All architectures support ThinLTO now. However, this code is kept for the parts := strings.Split(c.Triple(), "-")
// time being in case there are regressions. The non-ThinLTO code support if parts[0] == "wasm32" {
// should be removed when it is proven to work reliably. // wasm-ld doesn't seem to support ThinLTO yet.
return false
}
if parts[0] == "avr" || parts[0] == "xtensa" {
// These use external (GNU) linkers which might perhaps support ThinLTO
// through a plugin, but it's too much hassle to set up.
return false
}
// Other architectures support ThinLTO.
return true return true
} }
@@ -225,9 +230,6 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
if c.CPU() != "" { if c.CPU() != "" {
archname += "-" + c.CPU() archname += "-" + c.CPU()
} }
if c.ABI() != "" {
archname += "-" + c.ABI()
}
// Try to load a precompiled library. // Try to load a precompiled library.
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name) precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
@@ -318,7 +320,7 @@ func (c *Config) CFlags() []string {
panic("unknown libc: " + c.Target.Libc) panic("unknown libc: " + c.Target.Libc)
} }
// Always emit debug information. It is optionally stripped at link time. // Always emit debug information. It is optionally stripped at link time.
cflags = append(cflags, "-gdwarf-4") cflags = append(cflags, "-g")
// Use the same optimization level as TinyGo. // Use the same optimization level as TinyGo.
cflags = append(cflags, "-O"+c.Options.Opt) cflags = append(cflags, "-O"+c.Options.Opt)
// Set the LLVM target triple. // Set the LLVM target triple.
@@ -336,10 +338,6 @@ func (c *Config) CFlags() []string {
cflags = append(cflags, "-mcpu="+c.Target.CPU) cflags = append(cflags, "-mcpu="+c.Target.CPU)
} }
} }
// Set the -mabi flag, if needed.
if c.ABI() != "" {
cflags = append(cflags, "-mabi="+c.ABI())
}
return cflags return cflags
} }
@@ -379,8 +377,8 @@ func (c *Config) VerifyIR() bool {
} }
// Debug returns whether debug (DWARF) information should be retained by the // Debug returns whether debug (DWARF) information should be retained by the
// linker. By default, debug information is retained, but it can be removed // linker. By default, debug information is retained but it can be removed with
// with the -no-debug flag. // the -no-debug flag.
func (c *Config) Debug() bool { func (c *Config) Debug() bool {
return c.Options.Debug return c.Options.Debug
} }
@@ -500,8 +498,12 @@ func (c *Config) RelocationModel() string {
return "static" return "static"
} }
// WasmAbi returns the WASM ABI which is specified in the target JSON file. // WasmAbi returns the WASM ABI which is specified in the target JSON file, and
// the value is overridden by `-wasm-abi` flag if it is provided
func (c *Config) WasmAbi() string { func (c *Config) WasmAbi() string {
if c.Options.WasmAbi != "" {
return c.Options.WasmAbi
}
return c.Target.WasmAbi return c.Target.WasmAbi
} }
@@ -538,8 +540,6 @@ func (c *Config) Emulator(format, binary string) ([]string, error) {
var emulator []string var emulator []string
for _, s := range parts { for _, s := range parts {
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")) s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
// Allow replacement of what's usually /tmp except notably Windows.
s = strings.ReplaceAll(s, "{tmpDir}", os.TempDir())
s = strings.ReplaceAll(s, "{"+format+"}", binary) s = strings.ReplaceAll(s, "{"+format+"}", binary)
emulator = append(emulator, s) emulator = append(emulator, s)
} }
+2 -2
View File
@@ -8,7 +8,7 @@ import (
) )
var ( var (
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"} validGCOptions = []string{"none", "leaking", "conservative"}
validSchedulerOptions = []string{"none", "tasks", "asyncify"} validSchedulerOptions = []string{"none", "tasks", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb"} validSerialOptions = []string{"none", "uart", "usb"}
validPrintSizeOptions = []string{"none", "short", "full"} validPrintSizeOptions = []string{"none", "short", "full"}
@@ -42,6 +42,7 @@ type Options struct {
PrintAllocs *regexp.Regexp // regexp string PrintAllocs *regexp.Regexp // regexp string
PrintStacks bool PrintStacks bool
Tags []string Tags []string
WasmAbi string
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
TestConfig TestConfig TestConfig TestConfig
Programmer string Programmer string
@@ -51,7 +52,6 @@ type Options struct {
PrintJSON bool PrintJSON bool
Monitor bool Monitor bool
BaudRate int BaudRate int
Timeout time.Duration
} }
// Verify performs a validation on the given options, raising an error if options are not valid. // Verify performs a validation on the given options, raising an error if options are not valid.
+1 -7
View File
@@ -9,7 +9,7 @@ import (
func TestVerifyOptions(t *testing.T) { func TestVerifyOptions(t *testing.T) {
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`) expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`) expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`) expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`) expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
@@ -48,12 +48,6 @@ func TestVerifyOptions(t *testing.T) {
GC: "conservative", GC: "conservative",
}, },
}, },
{
name: "GCOptionCustom",
opts: compileopts.Options{
GC: "custom",
},
},
{ {
name: "InvalidSchedulerOption", name: "InvalidSchedulerOption",
opts: compileopts.Options{ opts: compileopts.Options{
+4 -18
View File
@@ -26,7 +26,6 @@ type TargetSpec struct {
Inherits []string `json:"inherits"` Inherits []string `json:"inherits"`
Triple string `json:"llvm-target"` Triple string `json:"llvm-target"`
CPU string `json:"cpu"` CPU string `json:"cpu"`
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag
Features string `json:"features"` Features string `json:"features"`
GOOS string `json:"goos"` GOOS string `json:"goos"`
GOARCH string `json:"goarch"` GOARCH string `json:"goarch"`
@@ -48,7 +47,7 @@ type TargetSpec struct {
FlashCommand string `json:"flash-command"` FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"` GDB []string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"` PortReset string `json:"flash-1200-bps-reset"`
SerialPort []string `json:"serial-port"` // serial port IDs in the form "vid:pid" SerialPort []string `json:"serial-port"` // serial port IDs in the form "acm:vid:pid" or "usb:vid:pid"
FlashMethod string `json:"flash-method"` FlashMethod string `json:"flash-method"`
FlashVolume string `json:"msd-volume-name"` FlashVolume string `json:"msd-volume-name"`
FlashFilename string `json:"msd-firmware-name"` FlashFilename string `json:"msd-firmware-name"`
@@ -195,7 +194,6 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
default: default:
llvmarch = options.GOARCH llvmarch = options.GOARCH
} }
llvmvendor := "unknown"
llvmos := options.GOOS llvmos := options.GOOS
if llvmos == "darwin" { if llvmos == "darwin" {
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer // Use macosx* instead of darwin, otherwise darwin/arm64 will refer
@@ -205,14 +203,12 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
// Looks like Apple prefers to call this architecture ARM64 // Looks like Apple prefers to call this architecture ARM64
// instead of AArch64. // instead of AArch64.
llvmarch = "arm64" llvmarch = "arm64"
llvmos = "macosx11.0.0"
} }
llvmvendor = "apple"
} }
// Target triples (which actually have four components, but are called // Target triples (which actually have four components, but are called
// triples for historical reasons) have the form: // triples for historical reasons) have the form:
// arch-vendor-os-environment // arch-vendor-os-environment
target := llvmarch + "-" + llvmvendor + "-" + llvmos target := llvmarch + "-unknown-" + llvmos
if options.GOOS == "windows" { if options.GOOS == "windows" {
target += "-gnu" target += "-gnu"
} else if options.GOARCH == "arm" { } else if options.GOARCH == "arm" {
@@ -250,7 +246,6 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
GOOS: goos, GOOS: goos,
GOARCH: goarch, GOARCH: goarch,
BuildTags: []string{goos, goarch}, BuildTags: []string{goos, goarch},
GC: "precise",
Scheduler: "tasks", Scheduler: "tasks",
Linker: "cc", Linker: "cc",
DefaultStackSize: 1024 * 64, // 64kB DefaultStackSize: 1024 * 64, // 64kB
@@ -304,19 +299,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// normally present in Go (without explicitly opting in). // normally present in Go (without explicitly opting in).
// For more discussion: // For more discussion:
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1 // https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
switch goarch {
case "amd64":
spec.LDFlags = append(spec.LDFlags,
"-m", "i386pep",
"--image-base", "0x400000",
)
case "arm64":
spec.LDFlags = append(spec.LDFlags,
"-m", "arm64pe",
)
}
spec.LDFlags = append(spec.LDFlags, spec.LDFlags = append(spec.LDFlags,
"-m", "i386pep",
"-Bdynamic", "-Bdynamic",
"--image-base", "0x400000",
"--gc-sections", "--gc-sections",
"--no-insert-timestamp", "--no-insert-timestamp",
"--no-dynamicbase", "--no-dynamicbase",
+1 -1
View File
@@ -52,7 +52,7 @@ func (b *builder) createAlias(alias llvm.Value) {
b.CreateUnreachable() b.CreateUnreachable()
return return
} }
result := b.CreateCall(alias.GlobalValueType(), alias, b.llvmFn.Params(), "") result := b.CreateCall(alias, b.llvmFn.Params(), "")
if result.Type().TypeKind() == llvm.VoidTypeKind { if result.Type().TypeKind() == llvm.VoidTypeKind {
b.CreateRetVoid() b.CreateRetVoid()
} else { } else {
+7 -8
View File
@@ -89,11 +89,10 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic") b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
} }
// createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice // createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
// and unsafe.String. This function must panic if the ptr/len parameters are // function must panic if the ptr/len parameters are invalid.
// invalid. func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value, elementType llvm.Type, lenType *types.Basic) { // From the documentation of unsafe.Slice:
// From the documentation of unsafe.Slice and unsafe.String:
// > At run time, if len is negative, or if ptr is nil and len is not // > At run time, if len is negative, or if ptr is nil and len is not
// > zero, a run-time panic occurs. // > zero, a run-time panic occurs.
// However, in practice, it is also necessary to check that the length is // However, in practice, it is also necessary to check that the length is
@@ -106,7 +105,7 @@ func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value,
// Determine the maximum slice size, and therefore the maximum value of the // Determine the maximum slice size, and therefore the maximum value of the
// len parameter. // len parameter.
maxSize := b.maxSliceSize(elementType) maxSize := b.maxSliceSize(ptr.Type().ElementType())
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false) maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
// Do the check. By using unsigned greater than for the length check, signed // Do the check. By using unsigned greater than for the length check, signed
@@ -118,7 +117,7 @@ func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value,
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "") lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "") assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
assert = b.CreateOr(assert, lenOutOfBounds, "") assert = b.CreateOr(assert, lenOutOfBounds, "")
b.createRuntimeAssert(assert, name, "unsafeSlicePanic") b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
} }
// createChanBoundsCheck creates a bounds check before creating a new channel to // createChanBoundsCheck creates a bounds check before creating a new channel to
@@ -146,7 +145,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
} }
// Make the maxBufSize actually the maximum allowed value (in number of // Make the maxBufSize actually the maximum allowed value (in number of
// elements in the channel buffer). // elements in the channel buffer).
maxBufSize = b.CreateUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false), "") maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false))
// Make sure maxBufSize has the same type as bufSize. // Make sure maxBufSize has the same type as bufSize.
if maxBufSize.Type() != bufSize.Type() { if maxBufSize.Type() != bufSize.Type() {
+3 -3
View File
@@ -25,7 +25,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
if fn.IsNil() { if fn.IsNil() {
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false)) fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false))
} }
oldVal := b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val}, "") oldVal := b.createCall(fn, []llvm.Value{ptr, val}, "")
// Return the new value, not the original value returned. // Return the new value, not the original value returned.
return b.CreateAdd(oldVal, val, "") return b.CreateAdd(oldVal, val, "")
} }
@@ -56,7 +56,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
return swapped return swapped
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer": case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
ptr := b.getValue(b.fn.Params[0]) ptr := b.getValue(b.fn.Params[0])
val := b.CreateLoad(b.getLLVMType(b.fn.Signature.Results().At(0).Type()), ptr, "") val := b.CreateLoad(ptr, "")
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent) val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
return val return val
@@ -78,7 +78,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
if fn.IsNil() { if fn.IsNil() {
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false)) fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
} }
b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "") b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
return llvm.Value{} return llvm.Value{}
} }
store := b.CreateStore(val, ptr) store := b.CreateStore(val, ptr)
+41 -31
View File
@@ -20,7 +20,7 @@ const maxFieldsPerParam = 3
type paramInfo struct { type paramInfo struct {
llvmType llvm.Type llvmType llvm.Type
name string // name, possibly with suffixes for e.g. struct fields name string // name, possibly with suffixes for e.g. struct fields
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer flags paramFlags
} }
// paramFlags identifies parameter attributes for flags. Most importantly, it // paramFlags identifies parameter attributes for flags. Most importantly, it
@@ -37,15 +37,15 @@ const (
// createRuntimeInvoke instead. // createRuntimeInvoke instead.
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value { func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function) fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
fnType, llvmFn := b.getFunction(fn) llvmFn := b.getFunction(fn)
if llvmFn.IsNil() { if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil)) panic("trying to call non-existent function: " + fn.RelString(nil))
} }
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
if isInvoke { if isInvoke {
return b.createInvoke(fnType, llvmFn, args, name) return b.createInvoke(llvmFn, args, name)
} }
return b.createCall(fnType, llvmFn, args, name) return b.createCall(llvmFn, args, name)
} }
// createRuntimeCall creates a new call to runtime.<fnName> with the given // createRuntimeCall creates a new call to runtime.<fnName> with the given
@@ -65,22 +65,22 @@ func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name str
// createCall creates a call to the given function with the arguments possibly // createCall creates a call to the given function with the arguments possibly
// expanded. // expanded.
func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value { func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
expanded := make([]llvm.Value, 0, len(args)) expanded := make([]llvm.Value, 0, len(args))
for _, arg := range args { for _, arg := range args {
fragments := b.expandFormalParam(arg) fragments := b.expandFormalParam(arg)
expanded = append(expanded, fragments...) expanded = append(expanded, fragments...)
} }
return b.CreateCall(fnType, fn, expanded, name) return b.CreateCall(fn, expanded, name)
} }
// createInvoke is like createCall but continues execution at the landing pad if // createInvoke is like createCall but continues execution at the landing pad if
// the call resulted in a panic. // the call resulted in a panic.
func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value { func (b *builder) createInvoke(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
if b.hasDeferFrame() { if b.hasDeferFrame() {
b.createInvokeCheckpoint() b.createInvokeCheckpoint()
} }
return b.createCall(fnType, fn, args, name) return b.createCall(fn, args, name)
} }
// Expand an argument type to a list that can be used in a function call // Expand an argument type to a list that can be used in a function call
@@ -96,7 +96,13 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
// failed to expand this parameter: too many fields // failed to expand this parameter: too many fields
} }
// TODO: split small arrays // TODO: split small arrays
return []paramInfo{c.getParamInfo(t, name, goType)} return []paramInfo{
{
llvmType: t,
name: name,
flags: getTypeFlags(goType),
},
}
} }
// expandFormalParamOffsets returns a list of offsets from the start of an // expandFormalParamOffsets returns a list of offsets from the start of an
@@ -146,6 +152,7 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
// Try to flatten a struct type to a list of types. Returns a 1-element slice // Try to flatten a struct type to a list of types. Returns a 1-element slice
// with the passed in type if this is not possible. // with the passed in type if this is not possible.
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo { func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
typeFlags := getTypeFlags(goType)
switch t.TypeKind() { switch t.TypeKind() {
case llvm.StructTypeKind: case llvm.StructTypeKind:
var paramInfos []paramInfo var paramInfos []paramInfo
@@ -176,37 +183,40 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
} }
} }
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i)) subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
for i := range subInfos {
subInfos[i].flags |= typeFlags
}
paramInfos = append(paramInfos, subInfos...) paramInfos = append(paramInfos, subInfos...)
} }
return paramInfos return paramInfos
default: default:
return []paramInfo{c.getParamInfo(t, name, goType)} return []paramInfo{
{
llvmType: t,
name: name,
flags: typeFlags,
},
}
} }
} }
// getParamInfo collects information about a parameter. For example, if this // getTypeFlags returns the type flags for a given type. It will not recurse
// parameter is pointer-like, it will also store the element type for the // into sub-types (such as in structs).
// dereferenceable_or_null attribute. func getTypeFlags(t types.Type) paramFlags {
func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Type) paramInfo { if t == nil {
info := paramInfo{ return 0
llvmType: t,
name: name,
} }
if goType != nil { switch t.Underlying().(type) {
switch underlying := goType.Underlying().(type) { case *types.Pointer:
case *types.Pointer: // Pointers in Go must either point to an object or be nil.
// Pointers in Go must either point to an object or be nil. return paramIsDeferenceableOrNull
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMType(underlying.Elem())) case *types.Chan, *types.Map:
case *types.Chan: // Channels and maps are implemented as pointers pointing to some
// Channels are implemented simply as a *runtime.channel. // object, and follow the same rules as *types.Pointer.
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("channel")) return paramIsDeferenceableOrNull
case *types.Map: default:
// Maps are similar to channels: they are implemented as a return 0
// *runtime.hashmap.
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("hashmap"))
}
} }
return info
} }
// extractSubfield extracts a field from a struct, or returns null if this is // extractSubfield extracts a field from a struct, or returns null if this is
+10 -10
View File
@@ -43,7 +43,7 @@ func (b *builder) createChanSend(instr *ssa.Send) {
} }
// Allocate blockedlist buffer. // Allocate blockedlist buffer.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList") channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the send. // Do the send.
@@ -75,7 +75,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
} }
// Allocate blockedlist buffer. // Allocate blockedlist buffer.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList") channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the receive. // Do the receive.
@@ -84,7 +84,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
if isZeroSize { if isZeroSize {
received = llvm.ConstNull(valueType) received = llvm.ConstNull(valueType)
} else { } else {
received = b.CreateLoad(valueType, valueAlloca, "chan.received") received = b.CreateLoad(valueAlloca, "chan.received")
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
} }
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize) b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
@@ -173,7 +173,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize)) allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca") recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
recvbufAlloca.SetAlignment(recvbufAlign) recvbufAlloca.SetAlignment(recvbufAlign)
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{ recvbuf = b.CreateGEP(recvbufAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
}, "select.recvbuf") }, "select.recvbuf")
@@ -184,13 +184,13 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca") statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
for i, state := range selectStates { for i, state := range selectStates {
// Set each slice element to the appropriate channel. // Set each slice element to the appropriate channel.
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{ gep := b.CreateGEP(statesAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false), llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}, "") }, "")
b.CreateStore(state, gep) b.CreateStore(state, gep)
} }
statesPtr := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{ statesPtr := b.CreateGEP(statesAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
}, "select.states") }, "select.states")
@@ -204,7 +204,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates)) chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca") chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false) chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{ chBlockPtr := b.CreateGEP(chBlockAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
}, "select.block") }, "select.block")
@@ -264,8 +264,8 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
// receive can proceed at a time) so we'll get that alloca, bitcast // receive can proceed at a time) so we'll get that alloca, bitcast
// it to the correct type, and dereference it. // it to the correct type, and dereference it.
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)] recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
typ := b.getLLVMType(expr.Type()) typ := llvm.PointerType(b.getLLVMType(expr.Type()), 0)
ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "") ptr := b.CreateBitCast(recvbuf, typ, "")
return b.CreateLoad(typ, ptr, "") return b.CreateLoad(ptr, "")
} }
} }
+106 -179
View File
@@ -41,7 +41,6 @@ type Config struct {
Triple string Triple string
CPU string CPU string
Features string Features string
ABI string
GOOS string GOOS string
GOARCH string GOARCH string
CodeModel string CodeModel string
@@ -64,7 +63,6 @@ type compilerContext struct {
DumpSSA bool DumpSSA bool
mod llvm.Module mod llvm.Module
ctx llvm.Context ctx llvm.Context
builder llvm.Builder // only used for constant operations
dibuilder *llvm.DIBuilder dibuilder *llvm.DIBuilder
cu llvm.Metadata cu llvm.Metadata
difiles map[string]llvm.Metadata difiles map[string]llvm.Metadata
@@ -76,7 +74,6 @@ type compilerContext struct {
i8ptrType llvm.Type // for convenience i8ptrType llvm.Type // for convenience
rawVoidFuncType llvm.Type // for convenience rawVoidFuncType llvm.Type // for convenience
funcPtrAddrSpace int funcPtrAddrSpace int
hasTypedPointers bool // for LLVM 14 backwards compatibility
uintptrType llvm.Type uintptrType llvm.Type
program *ssa.Program program *ssa.Program
diagnostics []error diagnostics []error
@@ -101,7 +98,6 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
} }
c.ctx = llvm.NewContext() c.ctx = llvm.NewContext()
c.builder = c.ctx.NewBuilder()
c.mod = c.ctx.NewModule(moduleName) c.mod = c.ctx.NewModule(moduleName)
c.mod.SetTarget(config.Triple) c.mod.SetTarget(config.Triple)
c.mod.SetDataLayout(c.targetData.String()) c.mod.SetDataLayout(c.targetData.String())
@@ -124,25 +120,17 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false) dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType) dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace() c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
c.hasTypedPointers = c.i8ptrType != llvm.PointerType(c.ctx.Int16Type(), 0) // with opaque pointers, all pointers are the same type (LLVM 15+)
c.rawVoidFuncType = dummyFunc.Type() c.rawVoidFuncType = dummyFunc.Type()
dummyFunc.EraseFromParentAsFunction() dummyFunc.EraseFromParentAsFunction()
return c return c
} }
// Dispose everything related to the context, _except_ for the IR module (and
// the associated context).
func (c *compilerContext) dispose() {
c.builder.Dispose()
}
// builder contains all information relevant to build a single function. // builder contains all information relevant to build a single function.
type builder struct { type builder struct {
*compilerContext *compilerContext
llvm.Builder llvm.Builder
fn *ssa.Function fn *ssa.Function
llvmFnType llvm.Type
llvmFn llvm.Value llvmFn llvm.Value
info functionInfo info functionInfo
locals map[ssa.Value]llvm.Value // local variables locals map[ssa.Value]llvm.Value // local variables
@@ -152,7 +140,6 @@ type builder struct {
phis []phiNode phis []phiNode
deferPtr llvm.Value deferPtr llvm.Value
deferFrame llvm.Value deferFrame llvm.Value
stackChainAlloca llvm.Value
landingpad llvm.BasicBlock landingpad llvm.BasicBlock
difunc llvm.Metadata difunc llvm.Metadata
dilocals map[*types.Var]llvm.Metadata dilocals map[*types.Var]llvm.Metadata
@@ -168,13 +155,11 @@ type builder struct {
} }
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder { func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
fnType, fn := c.getFunction(f)
return &builder{ return &builder{
compilerContext: c, compilerContext: c,
Builder: irbuilder, Builder: irbuilder,
fn: f, fn: f,
llvmFnType: fnType, llvmFn: c.getFunction(f),
llvmFn: fn,
info: c.getFunctionInfo(f), info: c.getFunctionInfo(f),
locals: make(map[ssa.Value]llvm.Value), locals: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata), dilocals: make(map[*types.Var]llvm.Metadata),
@@ -268,7 +253,6 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
// CompilePackage compiles a single package to a LLVM module. // CompilePackage compiles a single package to a LLVM module.
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) { 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 := newCompilerContext(moduleName, machine, config, dumpSSA)
defer c.dispose()
c.packageDir = pkg.OriginalDir() c.packageDir = pkg.OriginalDir()
c.embedGlobals = pkg.EmbedGlobals c.embedGlobals = pkg.EmbedGlobals
c.pkg = pkg.Pkg c.pkg = pkg.Pkg
@@ -325,18 +309,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
c.dibuilder.Destroy() c.dibuilder.Destroy()
} }
// Add the "target-abi" flag, which is necessary on RISC-V otherwise it will
// pick one that doesn't match the -mabi Clang flag.
if c.ABI != "" {
c.mod.AddNamedMetadataOperand("llvm.module.flags",
c.ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
c.ctx.MDString("target-abi"),
c.ctx.MDString(c.ABI),
}),
)
}
return c.mod, c.diagnostics return c.mod, c.diagnostics
} }
@@ -425,20 +397,14 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
} }
return c.getLLVMType(typ.Underlying()) return c.getLLVMType(typ.Underlying())
case *types.Pointer: case *types.Pointer:
if c.hasTypedPointers { ptrTo := c.getLLVMType(typ.Elem())
ptrTo := c.getLLVMType(typ.Elem()) return llvm.PointerType(ptrTo, 0)
return llvm.PointerType(ptrTo, 0)
}
return c.i8ptrType // all pointers are the same
case *types.Signature: // function value case *types.Signature: // function value
return c.getFuncType(typ) return c.getFuncType(typ)
case *types.Slice: case *types.Slice:
ptrType := c.i8ptrType elemType := c.getLLVMType(typ.Elem())
if c.hasTypedPointers {
ptrType = llvm.PointerType(c.getLLVMType(typ.Elem()), 0)
}
members := []llvm.Type{ members := []llvm.Type{
ptrType, llvm.PointerType(elemType, 0),
c.uintptrType, // len c.uintptrType, // len
c.uintptrType, // cap c.uintptrType, // cap
} }
@@ -745,8 +711,7 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
// DISubprogram metadata node. // DISubprogram metadata node.
func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata { func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
pos := c.program.Fset.Position(f.Syntax().Pos()) pos := c.program.Fset.Position(f.Syntax().Pos())
_, fn := c.getFunction(f) return c.attachDebugInfoRaw(f, c.getFunction(f), "", pos.Filename, pos.Line)
return c.attachDebugInfoRaw(f, fn, "", pos.Filename, pos.Line)
} }
// attachDebugInfo adds debug info to a function declaration. It returns the // attachDebugInfo adds debug info to a function declaration. It returns the
@@ -819,7 +784,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
member := pkg.Members[name] member := pkg.Members[name]
switch member := member.(type) { switch member := member.(type) {
case *ssa.Function: case *ssa.Function:
if member.TypeParams() != nil { if member.Synthetic == "generic function" {
// Do not try to build generic (non-instantiated) functions. // Do not try to build generic (non-instantiated) functions.
continue continue
} }
@@ -884,7 +849,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
if files, ok := c.embedGlobals[member.Name()]; ok { if files, ok := c.embedGlobals[member.Name()]; ok {
c.createEmbedGlobal(member, global, files) c.createEmbedGlobal(member, global, files)
} else if !info.extern { } else if !info.extern {
global.SetInitializer(llvm.ConstNull(global.GlobalValueType())) global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
global.SetVisibility(llvm.HiddenVisibility) global.SetVisibility(llvm.HiddenVisibility)
if info.section != "" { if info.section != "" {
global.SetSection(info.section) global.SetSection(info.section)
@@ -949,7 +914,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
bufferGlobal.SetInitializer(bufferValue) bufferGlobal.SetInitializer(bufferValue)
bufferGlobal.SetLinkage(llvm.InternalLinkage) bufferGlobal.SetLinkage(llvm.InternalLinkage)
bufferGlobal.SetAlignment(1) bufferGlobal.SetAlignment(1)
slicePtr := llvm.ConstInBoundsGEP(bufferValue.Type(), bufferGlobal, []llvm.Value{ slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(c.uintptrType, 0, false),
}) })
@@ -1003,10 +968,10 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
for _, file := range allFiles { for _, file := range allFiles {
fileStruct := llvm.ConstNull(embedFileStructType) fileStruct := llvm.ConstNull(embedFileStructType)
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String])) name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
fileStruct = c.builder.CreateInsertValue(fileStruct, name, 0, "") // "name" field fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
if file.Hash != "" { if file.Hash != "" {
data := c.getEmbedFileString(file) data := c.getEmbedFileString(file)
fileStruct = c.builder.CreateInsertValue(fileStruct, data, 1, "") // "data" field fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
} }
fileStructs = append(fileStructs, fileStruct) fileStructs = append(fileStructs, fileStruct)
} }
@@ -1021,7 +986,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
// Create the slice object itself. // Create the slice object itself.
// Because embed.FS refers to it as *[]embed.file instead of a plain // Because embed.FS refers to it as *[]embed.file instead of a plain
// []embed.file, we have to store this as a global. // []embed.file, we have to store this as a global.
slicePtr := llvm.ConstInBoundsGEP(sliceDataInitializer.Type(), sliceDataGlobal, []llvm.Value{ slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(c.uintptrType, 0, false),
}) })
@@ -1037,7 +1002,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
// Define the embed.FS struct. It has only one field: the files (as a // Define the embed.FS struct. It has only one field: the files (as a
// *[]embed.file). // *[]embed.file).
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem())) globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "") globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
global.SetInitializer(globalInitializer) global.SetInitializer(globalInitializer)
global.SetVisibility(llvm.HiddenVisibility) global.SetVisibility(llvm.HiddenVisibility)
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type())) global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
@@ -1049,11 +1014,11 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value { func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
dataGlobalName := "embed/file_" + file.Hash dataGlobalName := "embed/file_" + file.Hash
dataGlobal := c.mod.NamedGlobal(dataGlobalName) dataGlobal := c.mod.NamedGlobal(dataGlobalName)
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
if dataGlobal.IsNil() { if dataGlobal.IsNil() {
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName) dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
} }
strPtr := llvm.ConstInBoundsGEP(dataGlobalType, dataGlobal, []llvm.Value{ strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(c.uintptrType, 0, false),
}) })
@@ -1099,11 +1064,6 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// otherwise the function is not exported. // otherwise the function is not exported.
functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName) functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)
b.llvmFn.AddFunctionAttr(functionAttr) b.llvmFn.AddFunctionAttr(functionAttr)
// Unlike most targets, exported functions are actually visible in
// WebAssembly (even if it's not called from within the WebAssembly
// module). But LTO generally optimizes such functions away. Therefore,
// exported functions must be explicitly marked as used.
llvmutil.AppendToGlobal(b.mod, "llvm.used", b.llvmFn)
} }
// Some functions have a pragma controlling the inlining level. // Some functions have a pragma controlling the inlining level.
@@ -1232,13 +1192,6 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// them. // them.
b.deferInitFunc() b.deferInitFunc()
} }
if b.NeedsStackObjects {
// Create a dummy alloca that will be used in runtime.trackPointer.
// It is necessary to pass a dummy alloca to runtime.trackPointer
// because runtime.trackPointer is replaced by an alloca store.
b.stackChainAlloca = b.CreateAlloca(b.ctx.Int8Type(), "stackalloc")
}
} }
// createFunction builds the LLVM IR implementation for this function. The // createFunction builds the LLVM IR implementation for this function. The
@@ -1439,7 +1392,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
b.CreateRet(b.getValue(instr.Results[0])) b.CreateRet(b.getValue(instr.Results[0]))
} else { } else {
// Multiple return values. Put them all in a struct. // Multiple return values. Put them all in a struct.
retVal := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType()) retVal := llvm.ConstNull(b.llvmFn.Type().ElementType().ReturnType())
for i, result := range instr.Results { for i, result := range instr.Results {
val := b.getValue(result) val := b.getValue(result)
retVal = b.CreateInsertValue(retVal, val, i, "") retVal = b.CreateInsertValue(retVal, val, i, "")
@@ -1478,7 +1431,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf") elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr") elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr")
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen") elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := srcBuf.Type().ElementType()
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new") result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new")
newPtr := b.CreateExtractValue(result, 0, "append.newPtr") newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
@@ -1531,7 +1484,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen") srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray") dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray") srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := dstBuf.Type().ElementType()
dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr") dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr")
srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr") srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr")
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
@@ -1644,7 +1597,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
// Note: the pointer is always of type *i8. // Note: the pointer is always of type *i8.
ptr := argValues[0] ptr := argValues[0]
len := argValues[1] len := argValues[1]
return b.CreateGEP(b.ctx.Int8Type(), ptr, []llvm.Value{len}, ""), nil return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
case "Alignof": // unsafe.Alignof case "Alignof": // unsafe.Alignof
align := b.targetData.ABITypeAlignment(argValues[0].Type()) align := b.targetData.ABITypeAlignment(argValues[0].Type())
return llvm.ConstInt(b.uintptrType, uint64(align), false), nil return llvm.ConstInt(b.uintptrType, uint64(align), false), nil
@@ -1659,20 +1612,19 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
case "Sizeof": // unsafe.Sizeof case "Sizeof": // unsafe.Sizeof
size := b.targetData.TypeAllocSize(argValues[0].Type()) size := b.targetData.TypeAllocSize(argValues[0].Type())
return llvm.ConstInt(b.uintptrType, size, false), nil return llvm.ConstInt(b.uintptrType, size, false), nil
case "Slice", "String": // unsafe.Slice, unsafe.String case "Slice": // unsafe.Slice
// This creates a slice or string from a pointer and a length. // This creates a slice from a pointer and a length.
// Note that the exception mentioned in the documentation (if the // Note that the exception mentioned in the documentation (if the
// pointer and length are nil, the slice is also nil) is trivially // pointer and length are nil, the slice is also nil) is trivially
// already the case. // already the case.
ptr := argValues[0] ptr := argValues[0]
len := argValues[1] len := argValues[1]
var elementType llvm.Type slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
if callName == "Slice" { ptr.Type(),
elementType = b.getLLVMType(argTypes[0].Underlying().(*types.Pointer).Elem()) b.uintptrType,
} else { b.uintptrType,
elementType = b.ctx.Int8Type() }, false))
} b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
b.createUnsafeSliceStringCheck("unsafe."+callName, ptr, len, elementType, argTypes[1].Underlying().(*types.Basic))
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
// Too small, zero-extend len. // Too small, zero-extend len.
len = b.CreateZExt(len, b.uintptrType, "") len = b.CreateZExt(len, b.uintptrType, "")
@@ -1680,24 +1632,10 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
// Too big, truncate len. // Too big, truncate len.
len = b.CreateTrunc(len, b.uintptrType, "") len = b.CreateTrunc(len, b.uintptrType, "")
} }
if callName == "Slice" { slice = b.CreateInsertValue(slice, ptr, 0, "")
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{ slice = b.CreateInsertValue(slice, len, 1, "")
ptr.Type(), slice = b.CreateInsertValue(slice, len, 2, "")
b.uintptrType, return slice, nil
b.uintptrType,
}, false))
slice = b.CreateInsertValue(slice, ptr, 0, "")
slice = b.CreateInsertValue(slice, len, 1, "")
slice = b.CreateInsertValue(slice, len, 2, "")
return slice, nil
} else {
str := llvm.Undef(b.getLLVMRuntimeType("_string"))
str = b.CreateInsertValue(str, argValues[0], 0, "")
str = b.CreateInsertValue(str, len, 1, "")
return str, nil
}
case "SliceData", "StringData": // unsafe.SliceData, unsafe.StringData
return b.CreateExtractValue(argValues[0], 0, "slice.data"), nil
default: default:
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName) return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
} }
@@ -1716,7 +1654,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
// Try to call the function directly for trivially static calls. // Try to call the function directly for trivially static calls.
var callee, context llvm.Value var callee, context llvm.Value
var calleeType llvm.Type
exported := false exported := false
if fn := instr.StaticCallee(); fn != nil { if fn := instr.StaticCallee(); fn != nil {
// Direct function call, either to a named or anonymous (directly // Direct function call, either to a named or anonymous (directly
@@ -1747,7 +1684,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.createInterruptGlobal(instr) return b.createInterruptGlobal(instr)
} }
calleeType, callee = b.getFunction(fn) callee = b.getFunction(fn)
info := b.getFunctionInfo(fn) info := b.getFunctionInfo(fn)
if callee.IsNil() { if callee.IsNil() {
return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+info.linkName) return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+info.linkName)
@@ -1761,8 +1698,8 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
// Eventually we might be able to eliminate this special case // Eventually we might be able to eliminate this special case
// entirely. For details, see: // entirely. For details, see:
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521 // https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false) fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(calleeType, b.funcPtrAddrSpace)) callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
} }
case *ssa.MakeClosure: case *ssa.MakeClosure:
// A call on a func value, but the callee is trivial to find. For // A call on a func value, but the callee is trivial to find. For
@@ -1789,14 +1726,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
params = append([]llvm.Value{value}, params...) params = append([]llvm.Value{value}, params...)
params = append(params, typecode) params = append(params, typecode)
callee = b.getInvokeFunction(instr) callee = b.getInvokeFunction(instr)
calleeType = callee.GlobalValueType()
context = llvm.Undef(b.i8ptrType) context = llvm.Undef(b.i8ptrType)
} else { } else {
// Function pointer. // Function pointer.
value := b.getValue(instr.Value) value := b.getValue(instr.Value)
// This is a func value, which cannot be called directly. We have to // This is a func value, which cannot be called directly. We have to
// extract the function pointer and context first from the func value. // extract the function pointer and context first from the func value.
calleeType, callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
b.createNilCheck(instr.Value, callee, "fpcall") b.createNilCheck(instr.Value, callee, "fpcall")
} }
@@ -1806,7 +1742,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
params = append(params, context) params = append(params, context)
} }
return b.createInvoke(calleeType, callee, params, ""), nil return b.createInvoke(callee, params, ""), nil
} }
// getValue returns the LLVM value of a constant, function value, global, or // getValue returns the LLVM value of a constant, function value, global, or
@@ -1820,8 +1756,7 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String()) b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String())
return llvm.Undef(b.getLLVMType(expr.Type())) return llvm.Undef(b.getLLVMType(expr.Type()))
} }
_, fn := b.getFunction(expr) return b.createFuncValue(b.getFunction(expr), llvm.Undef(b.i8ptrType), expr.Signature)
return b.createFuncValue(fn, llvm.Undef(b.i8ptrType), expr.Signature)
case *ssa.Global: case *ssa.Global:
value := b.getGlobal(expr) value := b.getGlobal(expr)
if value.IsNil() { if value.IsNil() {
@@ -1969,77 +1904,46 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(expr.Field), false), llvm.ConstInt(b.ctx.Int32Type(), uint64(expr.Field), false),
} }
elementType := b.getLLVMType(expr.X.Type().Underlying().(*types.Pointer).Elem()) return b.CreateInBoundsGEP(val, indices, ""), nil
return b.CreateInBoundsGEP(elementType, val, indices, ""), nil
case *ssa.Function: case *ssa.Function:
panic("function is not an expression") panic("function is not an expression")
case *ssa.Global: case *ssa.Global:
panic("global is not an expression") panic("global is not an expression")
case *ssa.Index: case *ssa.Index:
collection := b.getValue(expr.X) array := b.getValue(expr.X)
index := b.getValue(expr.Index) index := b.getValue(expr.Index)
switch xType := expr.X.Type().Underlying().(type) { // Extend index to at least uintptr size, because getelementptr assumes
case *types.Basic: // extract byte from string // index is a signed integer.
// Value type must be a string, which is a basic type. index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
if xType.Info()&types.IsString == 0 {
panic("lookup on non-string?")
}
// Sometimes, the index can be e.g. an uint8 or int8, and we have to // Check bounds.
// correctly extend that type for two reasons: arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
// 1. The lookup bounds check expects an index of at least uintptr arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
// size. b.createLookupBoundsCheck(arrayLenLLVM, index)
// 2. getelementptr has signed operands, and therefore s[uint8(x)]
// can be lowered as s[int8(x)]. That would be a bug.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
// Bounds check. // Can't load directly from array (as index is non-constant), so have to
length := b.CreateExtractValue(collection, 1, "len") // do it using an alloca+gep+load.
b.createLookupBoundsCheck(length, index) alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(array.Type(), "index.alloca")
b.CreateStore(array, alloca)
// Lookup byte zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
buf := b.CreateExtractValue(collection, 0, "") ptr := b.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep")
bufElemType := b.ctx.Int8Type() result := b.CreateLoad(ptr, "index.load")
bufPtr := b.CreateInBoundsGEP(bufElemType, buf, []llvm.Value{index}, "") b.emitLifetimeEnd(allocaPtr, allocaSize)
return b.CreateLoad(bufElemType, bufPtr, ""), nil return result, nil
case *types.Array: // extract element from array
// Extend index to at least uintptr size, because getelementptr
// assumes index is a signed integer.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
// Check bounds.
arrayLen := llvm.ConstInt(b.uintptrType, uint64(xType.Len()), false)
b.createLookupBoundsCheck(arrayLen, index)
// Can't load directly from array (as index is non-constant), so
// have to do it using an alloca+gep+load.
arrayType := collection.Type()
alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
b.CreateStore(collection, alloca)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
b.emitLifetimeEnd(allocaPtr, allocaSize)
return result, nil
default:
panic("unknown *ssa.Index type")
}
case *ssa.IndexAddr: case *ssa.IndexAddr:
val := b.getValue(expr.X) val := b.getValue(expr.X)
index := b.getValue(expr.Index) index := b.getValue(expr.Index)
// Get buffer pointer and length // Get buffer pointer and length
var bufptr, buflen llvm.Value var bufptr, buflen llvm.Value
var bufType llvm.Type
switch ptrTyp := expr.X.Type().Underlying().(type) { switch ptrTyp := expr.X.Type().Underlying().(type) {
case *types.Pointer: case *types.Pointer:
typ := ptrTyp.Elem().Underlying() typ := expr.X.Type().Underlying().(*types.Pointer).Elem().Underlying()
switch typ := typ.(type) { switch typ := typ.(type) {
case *types.Array: case *types.Array:
bufptr = val bufptr = val
buflen = llvm.ConstInt(b.uintptrType, uint64(typ.Len()), false) buflen = llvm.ConstInt(b.uintptrType, uint64(typ.Len()), false)
bufType = b.getLLVMType(typ)
// Check for nil pointer before calculating the address, from // Check for nil pointer before calculating the address, from
// the spec: // the spec:
// > For an operand x of type T, the address operation &x // > For an operand x of type T, the address operation &x
@@ -2053,7 +1957,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
case *types.Slice: case *types.Slice:
bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr") bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr")
buflen = b.CreateExtractValue(val, 1, "indexaddr.len") buflen = b.CreateExtractValue(val, 1, "indexaddr.len")
bufType = b.getLLVMType(ptrTyp.Elem())
default: default:
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String()) return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
} }
@@ -2071,20 +1974,47 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
index, index,
} }
return b.CreateInBoundsGEP(bufType, bufptr, indices, ""), nil return b.CreateInBoundsGEP(bufptr, indices, ""), nil
case *types.Slice: case *types.Slice:
return b.CreateInBoundsGEP(bufType, bufptr, []llvm.Value{index}, ""), nil return b.CreateInBoundsGEP(bufptr, []llvm.Value{index}, ""), nil
default: default:
panic("unreachable") panic("unreachable")
} }
case *ssa.Lookup: // map lookup case *ssa.Lookup:
value := b.getValue(expr.X) value := b.getValue(expr.X)
index := b.getValue(expr.Index) index := b.getValue(expr.Index)
valueType := expr.Type() switch xType := expr.X.Type().Underlying().(type) {
if expr.CommaOk { case *types.Basic:
valueType = valueType.(*types.Tuple).At(0).Type() // Value type must be a string, which is a basic type.
if xType.Info()&types.IsString == 0 {
panic("lookup on non-string?")
}
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
// correctly extend that type for two reasons:
// 1. The lookup bounds check expects an index of at least uintptr
// size.
// 2. getelementptr has signed operands, and therefore s[uint8(x)]
// can be lowered as s[int8(x)]. That would be a bug.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
// Bounds check.
length := b.CreateExtractValue(value, 1, "len")
b.createLookupBoundsCheck(length, index)
// Lookup byte
buf := b.CreateExtractValue(value, 0, "")
bufPtr := b.CreateInBoundsGEP(buf, []llvm.Value{index}, "")
return b.CreateLoad(bufPtr, ""), nil
case *types.Map:
valueType := expr.Type()
if expr.CommaOk {
valueType = valueType.(*types.Tuple).At(0).Type()
}
return b.createMapLookup(xType.Key(), valueType, value, index, expr.CommaOk, expr.Pos())
default:
panic("unknown lookup type: " + expr.String())
} }
return b.createMapLookup(expr.X.Type().Underlying().(*types.Map).Key(), valueType, value, index, expr.CommaOk, expr.Pos())
case *ssa.MakeChan: case *ssa.MakeChan:
return b.createMakeChan(expr), nil return b.createMakeChan(expr), nil
case *ssa.MakeClosure: case *ssa.MakeClosure:
@@ -2208,8 +2138,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
switch typ := expr.X.Type().Underlying().(type) { switch typ := expr.X.Type().Underlying().(type) {
case *types.Pointer: // pointer to array case *types.Pointer: // pointer to array
// slice an array // slice an array
arrayType := typ.Elem().Underlying().(*types.Array) length := typ.Elem().Underlying().(*types.Array).Len()
length := arrayType.Len()
llvmLen := llvm.ConstInt(b.uintptrType, uint64(length), false) llvmLen := llvm.ConstInt(b.uintptrType, uint64(length), false)
if high.IsNil() { if high.IsNil() {
high = llvmLen high = llvmLen
@@ -2238,7 +2167,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
} }
sliceLen := b.CreateSub(high, low, "slice.len") sliceLen := b.CreateSub(high, low, "slice.len")
slicePtr := b.CreateInBoundsGEP(b.getLLVMType(arrayType), value, indices, "slice.ptr") slicePtr := b.CreateInBoundsGEP(value, indices, "slice.ptr")
sliceCap := b.CreateSub(max, low, "slice.cap") sliceCap := b.CreateSub(max, low, "slice.cap")
slice := b.ctx.ConstStruct([]llvm.Value{ slice := b.ctx.ConstStruct([]llvm.Value{
@@ -2277,8 +2206,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
max = b.CreateTrunc(max, b.uintptrType, "") max = b.CreateTrunc(max, b.uintptrType, "")
} }
ptrElemType := b.getLLVMType(typ.Elem()) newPtr := b.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
newPtr := b.CreateInBoundsGEP(ptrElemType, oldPtr, []llvm.Value{low}, "")
newLen := b.CreateSub(high, low, "") newLen := b.CreateSub(high, low, "")
newCap := b.CreateSub(max, low, "") newCap := b.CreateSub(max, low, "")
slice := b.ctx.ConstStruct([]llvm.Value{ slice := b.ctx.ConstStruct([]llvm.Value{
@@ -2318,7 +2246,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
high = b.CreateTrunc(high, b.uintptrType, "") high = b.CreateTrunc(high, b.uintptrType, "")
} }
newPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), oldPtr, []llvm.Value{low}, "") newPtr := b.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
newLen := b.CreateSub(high, low, "") newLen := b.CreateSub(high, low, "")
str := llvm.Undef(b.getLLVMRuntimeType("_string")) str := llvm.Undef(b.getLLVMRuntimeType("_string"))
str = b.CreateInsertValue(str, newPtr, 0, "") str = b.CreateInsertValue(str, newPtr, 0, "")
@@ -2781,15 +2709,14 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
var strPtr llvm.Value var strPtr llvm.Value
if str != "" { if str != "" {
objname := c.pkg.Path() + "$string" objname := c.pkg.Path() + "$string"
globalType := llvm.ArrayType(c.ctx.Int8Type(), len(str)) global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
global := llvm.AddGlobal(c.mod, globalType, objname)
global.SetInitializer(c.ctx.ConstString(str, false)) global.SetInitializer(c.ctx.ConstString(str, false))
global.SetLinkage(llvm.InternalLinkage) global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
global.SetAlignment(1) global.SetAlignment(1)
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero}) strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
} else { } else {
strPtr = llvm.ConstNull(c.i8ptrType) strPtr = llvm.ConstNull(c.i8ptrType)
} }
@@ -2814,15 +2741,15 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32])) 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])) 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.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
cplx = c.builder.CreateInsertValue(cplx, r, 0, "") cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
cplx = c.builder.CreateInsertValue(cplx, i, 1, "") cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
return cplx return cplx
} else if typ.Kind() == types.Complex128 { } else if typ.Kind() == types.Complex128 {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64])) 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])) 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.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
cplx = c.builder.CreateInsertValue(cplx, r, 0, "") cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
cplx = c.builder.CreateInsertValue(cplx, i, 1, "") cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
return cplx return cplx
} else { } else {
panic("unknown constant of basic type: " + expr.String()) panic("unknown constant of basic type: " + expr.String())
@@ -2909,7 +2836,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p
// create a GEP that is not in bounds. However, we're // create a GEP that is not in bounds. However, we're
// talking about unsafe code here so the programmer has to // talking about unsafe code here so the programmer has to
// be careful anyway. // be careful anyway.
return b.CreateInBoundsGEP(b.ctx.Int8Type(), origptr, []llvm.Value{index}, ""), nil return b.CreateInBoundsGEP(origptr, []llvm.Value{index}, ""), nil
} }
} }
} }
@@ -3142,10 +3069,10 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String()) return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
} }
case token.MUL: // *x, dereference pointer case token.MUL: // *x, dereference pointer
valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Pointer).Elem()) unop.X.Type().Underlying().(*types.Pointer).Elem()
if b.targetData.TypeAllocSize(valueType) == 0 { if b.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
// zero-length data // zero-length data
return llvm.ConstNull(valueType), nil return llvm.ConstNull(x.Type().ElementType()), nil
} else if strings.HasSuffix(unop.X.String(), "$funcaddr") { } else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
// CGo function pointer. The cgo part has rewritten CGo function // CGo function pointer. The cgo part has rewritten CGo function
// pointers as stub global variables of the form: // pointers as stub global variables of the form:
@@ -3153,14 +3080,14 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
// Instead of a load from the global, create a bitcast of the // Instead of a load from the global, create a bitcast of the
// function pointer itself. // function pointer itself.
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr") name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
_, fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function)) fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
if fn.IsNil() { if fn.IsNil() {
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name) return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
} }
return b.CreateBitCast(fn, b.i8ptrType, ""), nil return b.CreateBitCast(fn, b.i8ptrType, ""), nil
} else { } else {
b.createNilCheck(unop.X, x, "deref") b.createNilCheck(unop.X, x, "deref")
load := b.CreateLoad(valueType, x, "") load := b.CreateLoad(x, "")
return load, nil return load, nil
} }
case token.XOR: // ^x, toggle all bits in integer case token.XOR: // ^x, toggle all bits in integer
-11
View File
@@ -9,7 +9,6 @@ import (
"testing" "testing"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/loader" "github.com/tinygo-org/tinygo/loader"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -28,12 +27,6 @@ type testCase struct {
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
t.Parallel() t.Parallel()
// Determine Go minor version (e.g. 16 in go1.16.3).
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
if err != nil {
t.Fatal("could not read Go version:", err)
}
// Determine which tests to run, depending on the Go and LLVM versions. // Determine which tests to run, depending on the Go and LLVM versions.
tests := []testCase{ tests := []testCase{
{"basic.go", "", ""}, {"basic.go", "", ""},
@@ -50,9 +43,6 @@ func TestCompiler(t *testing.T) {
{"channel.go", "", ""}, {"channel.go", "", ""},
{"gc.go", "", ""}, {"gc.go", "", ""},
} }
if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""})
}
for _, tc := range tests { for _, tc := range tests {
name := tc.file name := tc.file
@@ -83,7 +73,6 @@ func TestCompiler(t *testing.T) {
compilerConfig := &Config{ compilerConfig := &Config{
Triple: config.Triple(), Triple: config.Triple(),
Features: config.Features(), Features: config.Features(),
ABI: config.ABI(),
GOOS: config.GOOS(), GOOS: config.GOOS(),
GOARCH: config.GOARCH(), GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(), CodeModel: config.CodeModel(),
+22 -30
View File
@@ -162,9 +162,9 @@ mov x0, #0
1: 1:
` `
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{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}" constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{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}"
if b.GOOS != "darwin" && b.GOOS != "windows" { if b.GOOS != "darwin" {
// These registers cause the following warning when compiling for // These registers cause the following warning when compiling for
// MacOS and Windows: // MacOS:
// warning: inline asm clobber list contains reserved registers: // warning: inline asm clobber list contains reserved registers:
// X18, FP // X18, FP
// Reserved registers on the clobber list may not be preserved // Reserved registers on the clobber list may not be preserved
@@ -201,7 +201,7 @@ li a0, 0
} }
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false) asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false) asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp") result := b.CreateCall(asm, []llvm.Value{b.deferFrame}, "setjmp")
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0)) result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result") isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
continueBB := b.insertBasicBlock("") continueBB := b.insertBasicBlock("")
@@ -249,8 +249,7 @@ func isInLoop(start *ssa.BasicBlock) bool {
func (b *builder) createDefer(instr *ssa.Defer) { func (b *builder) createDefer(instr *ssa.Defer) {
// The pointer to the previous defer struct, which we will replace to // The pointer to the previous defer struct, which we will replace to
// make a linked list. // make a linked list.
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) next := b.CreateLoad(b.deferPtr, "defer.next")
next := b.CreateLoad(deferType, b.deferPtr, "defer.next")
var values []llvm.Value var values []llvm.Value
valueTypes := []llvm.Type{b.uintptrType, next.Type()} valueTypes := []llvm.Type{b.uintptrType, next.Type()}
@@ -407,9 +406,6 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// createRunDefers emits code to run all deferred functions. // createRunDefers emits code to run all deferred functions.
func (b *builder) createRunDefers() { func (b *builder) createRunDefers() {
deferType := b.getLLVMRuntimeType("_defer")
deferPtrType := llvm.PointerType(deferType, 0)
// Add a loop like the following: // Add a loop like the following:
// for stack != nil { // for stack != nil {
// _stack := stack // _stack := stack
@@ -435,7 +431,7 @@ func (b *builder) createRunDefers() {
// Create loop head: // Create loop head:
// for stack != nil { // for stack != nil {
b.SetInsertPointAtEnd(loophead) b.SetInsertPointAtEnd(loophead)
deferData := b.CreateLoad(deferPtrType, b.deferPtr, "") deferData := b.CreateLoad(b.deferPtr, "")
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil") stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
b.CreateCondBr(stackIsNil, end, loop) b.CreateCondBr(stackIsNil, end, loop)
@@ -444,17 +440,17 @@ func (b *builder) createRunDefers() {
// stack = stack.next // stack = stack.next
// switch stack.callback { // switch stack.callback {
b.SetInsertPointAtEnd(loop) b.SetInsertPointAtEnd(loop)
nextStackGEP := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{ nextStackGEP := b.CreateInBoundsGEP(deferData, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
}, "stack.next.gep") }, "stack.next.gep")
nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next") nextStack := b.CreateLoad(nextStackGEP, "stack.next")
b.CreateStore(nextStack, b.deferPtr) b.CreateStore(nextStack, b.deferPtr)
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{ gep := b.CreateInBoundsGEP(deferData, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field
}, "callback.gep") }, "callback.gep")
callback := b.CreateLoad(b.uintptrType, gep, "callback") callback := b.CreateLoad(gep, "callback")
sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs)) sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs))
for i, callback := range b.allDeferFuncs { for i, callback := range b.allDeferFuncs {
@@ -490,13 +486,12 @@ func (b *builder) createRunDefers() {
forwardParams := []llvm.Value{} forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 2; i < len(valueTypes); i++ { for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []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(valueTypes[i], gep, "param") forwardParam := b.CreateLoad(gep, "param")
forwardParams = append(forwardParams, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
var fnPtr llvm.Value var fnPtr llvm.Value
var fnType llvm.Type
if !callback.IsInvoke() { if !callback.IsInvoke() {
// Isolate the func value. // Isolate the func value.
@@ -504,8 +499,8 @@ func (b *builder) createRunDefers() {
forwardParams = forwardParams[1:] forwardParams = forwardParams[1:]
//Get function pointer and context //Get function pointer and context
var context llvm.Value fp, context := b.decodeFuncValue(funcValue, callback.Signature())
fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature()) fnPtr = fp
//Pass context //Pass context
forwardParams = append(forwardParams, context) forwardParams = append(forwardParams, context)
@@ -514,7 +509,6 @@ func (b *builder) createRunDefers() {
// parameters. // parameters.
forwardParams = append(forwardParams[1:], forwardParams[0]) forwardParams = append(forwardParams[1:], forwardParams[0])
fnPtr = b.getInvokeFunction(callback) fnPtr = b.getInvokeFunction(callback)
fnType = fnPtr.GlobalValueType()
// Add the context parameter. An interface call cannot also be a // Add the context parameter. An interface call cannot also be a
// closure but we have to supply the parameter anyway for platforms // closure but we have to supply the parameter anyway for platforms
@@ -522,7 +516,7 @@ func (b *builder) createRunDefers() {
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
} }
b.createCall(fnType, fnPtr, forwardParams, "") b.createCall(fnPtr, forwardParams, "")
case *ssa.Function: case *ssa.Function:
// Direct call. // Direct call.
@@ -539,8 +533,8 @@ func (b *builder) createRunDefers() {
forwardParams := []llvm.Value{} forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := range getParams(callback.Signature) { for i := range getParams(callback.Signature) {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []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(valueTypes[i+2], gep, "param") forwardParam := b.CreateLoad(gep, "param")
forwardParams = append(forwardParams, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
@@ -553,8 +547,7 @@ func (b *builder) createRunDefers() {
} }
// Call real function. // Call real function.
fnType, fn := b.getFunction(callback) b.createInvoke(b.getFunction(callback), forwardParams, "")
b.createInvoke(fnType, fn, forwardParams, "")
case *ssa.MakeClosure: case *ssa.MakeClosure:
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
@@ -572,14 +565,13 @@ func (b *builder) createRunDefers() {
forwardParams := []llvm.Value{} forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 2; i < len(valueTypes); i++ { for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []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(valueTypes[i], gep, "param") forwardParam := b.CreateLoad(gep, "param")
forwardParams = append(forwardParams, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
// Call deferred function. // Call deferred function.
fnType, llvmFn := b.getFunction(fn) b.createCall(b.getFunction(fn), forwardParams, "")
b.createCall(fnType, llvmFn, forwardParams, "")
case *ssa.Builtin: case *ssa.Builtin:
db := b.deferBuiltinFuncs[callback] db := b.deferBuiltinFuncs[callback]
@@ -599,8 +591,8 @@ func (b *builder) createRunDefers() {
var argValues []llvm.Value var argValues []llvm.Value
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < params.Len(); i++ { for i := 0; i < params.Len(); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []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(valueTypes[i+2], gep, "param") forwardParam := b.CreateLoad(gep, "param")
argValues = append(argValues, forwardParam) argValues = append(argValues, forwardParam)
} }
+10 -13
View File
@@ -55,17 +55,15 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
// decodeFuncValue extracts the context and the function pointer from this func // decodeFuncValue extracts the context and the function pointer from this func
// value. This may be an expensive operation. // value. This may be an expensive operation.
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcType llvm.Type, funcPtr, context llvm.Value) { func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
context = b.CreateExtractValue(funcValue, 0, "") context = b.CreateExtractValue(funcValue, 0, "")
funcPtr = b.CreateExtractValue(funcValue, 1, "") bitcast := b.CreateExtractValue(funcValue, 1, "")
if !funcPtr.IsAConstantExpr().IsNil() && funcPtr.Opcode() == llvm.BitCast { if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
funcPtr = funcPtr.Operand(0) // needed for LLVM 14 (no opaque pointers) funcPtr = bitcast.Operand(0)
} return
if sig != nil {
funcType = b.getRawFuncType(sig)
llvmSig := llvm.PointerType(funcType, b.funcPtrAddrSpace)
funcPtr = b.CreateBitCast(funcPtr, llvmSig, "")
} }
llvmSig := b.getRawFuncType(sig)
funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
return return
} }
@@ -74,7 +72,7 @@ func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false) return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
} }
// getRawFuncType returns a LLVM function type for a given signature. // getRawFuncType returns a LLVM function pointer type for a given signature.
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type { func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
// Get the return type. // Get the return type.
var returnType llvm.Type var returnType llvm.Type
@@ -119,7 +117,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
paramTypes = append(paramTypes, c.i8ptrType) // context paramTypes = append(paramTypes, c.i8ptrType) // context
// Make a func type out of the signature. // Make a func type out of the signature.
return llvm.FunctionType(returnType, paramTypes, false) return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
} }
// parseMakeClosure makes a function value (with context) from the given // parseMakeClosure makes a function value (with context) from the given
@@ -143,6 +141,5 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
context := b.emitPointerPack(boundVars) context := b.emitPointerPack(boundVars)
// Create the closure. // Create the closure.
_, fn := b.getFunction(f) return b.createFuncValue(b.getFunction(f), context, f.Signature), nil
return b.createFuncValue(fn, context, f.Signature), nil
} }
+1 -1
View File
@@ -84,7 +84,7 @@ func (b *builder) trackPointer(value llvm.Value) {
if value.Type() != b.i8ptrType { if value.Type() != b.i8ptrType {
value = b.CreateBitCast(value, b.i8ptrType, "") value = b.CreateBitCast(value, b.i8ptrType, "")
} }
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "") b.createRuntimeCall("trackPointer", []llvm.Value{value}, "")
} }
// typeHasPointers returns whether this type is a pointer or contains pointers. // typeHasPointers returns whether this type is a pointer or contains pointers.
+27 -32
View File
@@ -7,6 +7,7 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -21,7 +22,6 @@ func (b *builder) createGo(instr *ssa.Go) {
var prefix string var prefix string
var funcPtr llvm.Value var funcPtr llvm.Value
var funcPtrType llvm.Type
hasContext := false hasContext := false
if callee := instr.Call.StaticCallee(); callee != nil { if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new // Static callee is known. This makes it easier to start a new
@@ -42,7 +42,7 @@ func (b *builder) createGo(instr *ssa.Go) {
params = append(params, context) // context parameter params = append(params, context) // context parameter
hasContext = true hasContext = true
} }
funcPtrType, funcPtr = b.getFunction(callee) funcPtr = b.getFunction(callee)
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
// We cheat. None of the builtins do any long or blocking operation, so // We cheat. None of the builtins do any long or blocking operation, so
// we might as well run these builtins right away without the program // we might as well run these builtins right away without the program
@@ -80,7 +80,6 @@ func (b *builder) createGo(instr *ssa.Go) {
itfTypeCode := b.CreateExtractValue(itf, 0, "") itfTypeCode := b.CreateExtractValue(itf, 0, "")
itfValue := b.CreateExtractValue(itf, 1, "") itfValue := b.CreateExtractValue(itf, 1, "")
funcPtr = b.getInvokeFunction(&instr.Call) funcPtr = b.getInvokeFunction(&instr.Call)
funcPtrType = funcPtr.GlobalValueType()
params = append([]llvm.Value{itfValue}, params...) // start with receiver params = append([]llvm.Value{itfValue}, params...) // start with receiver
params = append(params, itfTypeCode) // end with typecode params = append(params, itfTypeCode) // end with typecode
} else { } else {
@@ -90,7 +89,7 @@ func (b *builder) createGo(instr *ssa.Go) {
// * The function context, for closures. // * The function context, for closures.
// * The function pointer (for tasks). // * The function pointer (for tasks).
var context llvm.Value var context llvm.Value
funcPtrType, funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature)) funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
params = append(params, context, funcPtr) params = append(params, context, funcPtr)
hasContext = true hasContext = true
prefix = b.fn.RelString(nil) prefix = b.fn.RelString(nil)
@@ -98,14 +97,14 @@ func (b *builder) createGo(instr *ssa.Go) {
paramBundle := b.emitPointerPack(params) paramBundle := b.emitPointerPack(params)
var stackSize llvm.Value var stackSize llvm.Value
callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos()) callee := b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
if b.AutomaticStackSize { if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy // The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF // function that will be replaced with a load from a special ELF
// section that contains the stack size (and is modified after // section that contains the stack size (and is modified after
// linking). // linking).
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function)) stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize") stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
} else { } else {
// The stack size is fixed at compile time. By emitting it here as a // The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized. // constant, it can be optimized.
@@ -114,8 +113,8 @@ func (b *builder) createGo(instr *ssa.Go) {
} }
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false) stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
} }
fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function)) start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "") b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "")
} }
// createGoroutineStartWrapper creates a wrapper for the task-based // createGoroutineStartWrapper creates a wrapper for the task-based
@@ -141,19 +140,15 @@ func (b *builder) createGo(instr *ssa.Go) {
// to last parameter of the function) is used for this wrapper. If hasContext is // to last parameter of the function) is used for this wrapper. If hasContext is
// false, the parameter bundle is assumed to have no context parameter and undef // false, the parameter bundle is assumed to have no context parameter and undef
// is passed instead. // is passed instead.
func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value { func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
var wrapper llvm.Value var wrapper llvm.Value
b := &builder{ builder := c.ctx.NewBuilder()
compilerContext: c, defer builder.Dispose()
Builder: c.ctx.NewBuilder(),
}
defer b.Dispose()
var deadlock llvm.Value var deadlock llvm.Value
var deadlockType llvm.Type
if c.Scheduler == "asyncify" { if c.Scheduler == "asyncify" {
deadlockType, deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function)) deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
} }
if !fn.IsAFunction().IsNil() { if !fn.IsAFunction().IsNil() {
@@ -172,7 +167,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
wrapper.SetUnnamedAddr(true) wrapper.SetUnnamedAddr(true)
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name)) wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
entry := c.ctx.AddBasicBlock(wrapper, "entry") entry := c.ctx.AddBasicBlock(wrapper, "entry")
b.SetInsertPointAtEnd(entry) builder.SetInsertPointAtEnd(entry)
if c.Debug { if c.Debug {
pos := c.program.Fset.Position(pos) pos := c.program.Fset.Position(pos)
@@ -193,24 +188,24 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
Optimized: true, Optimized: true,
}) })
wrapper.SetSubprogram(difunc) wrapper.SetSubprogram(difunc)
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
} }
// Create the list of params for the call. // Create the list of params for the call.
paramTypes := fnType.ParamTypes() paramTypes := fn.Type().ElementType().ParamTypes()
if !hasContext { if !hasContext {
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
} }
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes) params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
if !hasContext { if !hasContext {
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
} }
// Create the call. // Create the call.
b.CreateCall(fnType, fn, params, "") builder.CreateCall(fn, params, "")
if c.Scheduler == "asyncify" { if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{ builder.CreateCall(deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
}, "") }, "")
} }
@@ -241,7 +236,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
wrapper.SetUnnamedAddr(true) wrapper.SetUnnamedAddr(true)
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", "")) wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
entry := c.ctx.AddBasicBlock(wrapper, "entry") entry := c.ctx.AddBasicBlock(wrapper, "entry")
b.SetInsertPointAtEnd(entry) builder.SetInsertPointAtEnd(entry)
if c.Debug { if c.Debug {
pos := c.program.Fset.Position(pos) pos := c.program.Fset.Position(pos)
@@ -262,23 +257,23 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
Optimized: true, Optimized: true,
}) })
wrapper.SetSubprogram(difunc) wrapper.SetSubprogram(difunc)
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
} }
// Get the list of parameters, with the extra parameters at the end. // Get the list of parameters, with the extra parameters at the end.
paramTypes := fnType.ParamTypes() paramTypes := fn.Type().ElementType().ParamTypes()
paramTypes = append(paramTypes, fn.Type()) // the last element is the function pointer paramTypes = append(paramTypes, fn.Type()) // the last element is the function pointer
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes) params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
// Get the function pointer. // Get the function pointer.
fnPtr := params[len(params)-1] fnPtr := params[len(params)-1]
params = params[:len(params)-1] params = params[:len(params)-1]
// Create the call. // Create the call.
b.CreateCall(fnType, fnPtr, params, "") builder.CreateCall(fnPtr, params, "")
if c.Scheduler == "asyncify" { if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{ builder.CreateCall(deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
}, "") }, "")
} }
@@ -286,13 +281,13 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if c.Scheduler == "asyncify" { if c.Scheduler == "asyncify" {
// The goroutine was terminated via deadlock. // The goroutine was terminated via deadlock.
b.CreateUnreachable() builder.CreateUnreachable()
} else { } else {
// Finish the function. Every basic block must end in a terminator, and // Finish the function. Every basic block must end in a terminator, and
// because goroutines never return a value we can simply return void. // because goroutines never return a value we can simply return void.
b.CreateRetVoid() builder.CreateRetVoid()
} }
// Return a ptrtoint of the wrapper, not the function itself. // Return a ptrtoint of the wrapper, not the function itself.
return b.CreatePtrToInt(wrapper, c.uintptrType, "") return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
} }
+8 -8
View File
@@ -25,7 +25,7 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
asm := constant.StringVal(args[0].(*ssa.Const).Value) asm := constant.StringVal(args[0].(*ssa.Const).Value)
target := llvm.InlineAsm(fnType, asm, "", true, false, 0, false) target := llvm.InlineAsm(fnType, asm, "", true, false, 0, false)
return b.CreateCall(fnType, target, nil, ""), nil return b.CreateCall(target, nil, ""), nil
} }
// This is a compiler builtin, which allows assembly to be called in a flexible // This is a compiler builtin, which allows assembly to be called in a flexible
@@ -120,7 +120,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
} }
fnType := llvm.FunctionType(outputType, argTypes, false) fnType := llvm.FunctionType(outputType, argTypes, false)
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0, false) target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0, false)
result := b.CreateCall(fnType, target, args, "") result := b.CreateCall(target, args, "")
if hasOutput { if hasOutput {
return result, nil return result, nil
} else { } else {
@@ -163,7 +163,7 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
constraints += ",~{r1},~{r2},~{r3}" constraints += ",~{r1},~{r2},~{r3}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false) target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
return b.CreateCall(fnType, target, llvmArgs, ""), nil return b.CreateCall(target, llvmArgs, ""), nil
} }
// This is a compiler builtin which emits an inline SVCall instruction. It can // This is a compiler builtin which emits an inline SVCall instruction. It can
@@ -201,7 +201,7 @@ func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}" constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false) target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
return b.CreateCall(fnType, target, llvmArgs, ""), nil return b.CreateCall(target, llvmArgs, ""), nil
} }
// This is a compiler builtin which emits CSR instructions. It can be one of: // This is a compiler builtin which emits CSR instructions. It can be one of:
@@ -226,24 +226,24 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, nil, false) fnType := llvm.FunctionType(b.uintptrType, nil, false)
asm := fmt.Sprintf("csrr $0, %d", csr) asm := fmt.Sprintf("csrr $0, %d", csr)
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0, false) target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0, false)
return b.CreateCall(fnType, target, nil, ""), nil return b.CreateCall(target, nil, ""), nil
case "Set": case "Set":
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
asm := fmt.Sprintf("csrw %d, $0", csr) asm := fmt.Sprintf("csrw %d, $0", csr)
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0, false) target := llvm.InlineAsm(fnType, asm, "r", true, false, 0, false)
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
case "SetBits": case "SetBits":
// Note: it may be possible to optimize this to csrrsi in many cases. // Note: it may be possible to optimize this to csrrsi in many cases.
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false) fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
asm := fmt.Sprintf("csrrs $0, %d, $1", csr) asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false) target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
case "ClearBits": case "ClearBits":
// Note: it may be possible to optimize this to csrrci in many cases. // Note: it may be possible to optimize this to csrrci in many cases.
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false) fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
asm := fmt.Sprintf("csrrc $0, %d, $1", csr) asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false) target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
default: default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name) return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
} }
+30 -29
View File
@@ -86,22 +86,22 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
if _, ok := typ.Underlying().(*types.Pointer); !ok { if _, ok := typ.Underlying().(*types.Pointer); !ok {
ptrTo = c.getTypeCode(types.NewPointer(typ)) ptrTo = c.getTypeCode(types.NewPointer(typ))
} }
globalValue := llvm.ConstNull(global.GlobalValueType()) globalValue := llvm.ConstNull(global.Type().ElementType())
if !references.IsNil() { if !references.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, references, 0, "") globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
} }
if length != 0 { if length != 0 {
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false) lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
globalValue = c.builder.CreateInsertValue(globalValue, lengthValue, 1, "") globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
} }
if !methodSet.IsNil() { if !methodSet.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, methodSet, 2, "") globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
} }
if !ptrTo.IsNil() { if !ptrTo.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, ptrTo, 3, "") globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
} }
if !typeAssert.IsNil() { if !typeAssert.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, typeAssert, 4, "") globalValue = llvm.ConstInsertValue(globalValue, typeAssert, []uint32{4})
} }
global.SetInitializer(globalValue) global.SetInitializer(globalValue)
global.SetLinkage(llvm.LinkOnceODRLinkage) global.SetLinkage(llvm.LinkOnceODRLinkage)
@@ -121,30 +121,30 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
structGlobalValue := llvm.ConstNull(structGlobalType) structGlobalValue := llvm.ConstNull(structGlobalType)
for i := 0; i < typ.NumFields(); i++ { for i := 0; i < typ.NumFields(); i++ {
fieldGlobalValue := llvm.ConstNull(runtimeStructField) fieldGlobalValue := llvm.ConstNull(runtimeStructField)
fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), 0, "") fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), []uint32{0})
fieldNameType, fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type()) fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type())
fieldName.SetLinkage(llvm.PrivateLinkage) fieldName.SetLinkage(llvm.PrivateLinkage)
fieldName.SetUnnamedAddr(true) fieldName.SetUnnamedAddr(true)
fieldName = llvm.ConstGEP(fieldNameType, fieldName, []llvm.Value{ fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 0, false),
}) })
fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldName, 1, "") fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
if typ.Tag(i) != "" { if typ.Tag(i) != "" {
fieldTagType, fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type()) fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type())
fieldTag.SetLinkage(llvm.PrivateLinkage) fieldTag.SetLinkage(llvm.PrivateLinkage)
fieldTag.SetUnnamedAddr(true) fieldTag.SetUnnamedAddr(true)
fieldTag = llvm.ConstGEP(fieldTagType, fieldTag, []llvm.Value{ fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 0, false),
}) })
fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldTag, 2, "") fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
} }
if typ.Field(i).Embedded() { if typ.Field(i).Embedded() {
fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false) fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldEmbedded, 3, "") fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldEmbedded, []uint32{3})
} }
structGlobalValue = c.builder.CreateInsertValue(structGlobalValue, fieldGlobalValue, i, "") structGlobalValue = llvm.ConstInsertValue(structGlobalValue, fieldGlobalValue, []uint32{uint32(i)})
} }
structGlobal.SetInitializer(structGlobalValue) structGlobal.SetInitializer(structGlobalValue)
structGlobal.SetUnnamedAddr(true) structGlobal.SetUnnamedAddr(true)
@@ -239,7 +239,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
if !global.IsNil() { if !global.IsNil() {
// the method set already exists // the method set already exists
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{zero, zero}) return llvm.ConstGEP(global, []llvm.Value{zero, zero})
} }
ms := c.program.MethodSets.MethodSet(typ) ms := c.program.MethodSets.MethodSet(typ)
@@ -254,12 +254,12 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
method := ms.At(i) method := ms.At(i)
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func)) signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
fn := c.program.MethodValue(method) fn := c.program.MethodValue(method)
llvmFnType, llvmFn := c.getFunction(fn) llvmFn := c.getFunction(fn)
if llvmFn.IsNil() { if llvmFn.IsNil() {
// compiler error, so panic // compiler error, so panic
panic("cannot find function: " + c.getFunctionInfo(fn).linkName) panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
} }
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn) wrapper := c.getInterfaceInvokeWrapper(fn, llvmFn)
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{ methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal, signatureGlobal,
llvm.ConstPtrToInt(wrapper, c.uintptrType), llvm.ConstPtrToInt(wrapper, c.uintptrType),
@@ -272,7 +272,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
global.SetInitializer(value) global.SetInitializer(value)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage) global.SetLinkage(llvm.LinkOnceODRLinkage)
return llvm.ConstGEP(arrayType, global, []llvm.Value{zero, zero}) return llvm.ConstGEP(global, []llvm.Value{zero, zero})
} }
// getInterfaceMethodSet returns a global variable with the method set of the // getInterfaceMethodSet returns a global variable with the method set of the
@@ -288,7 +288,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
if !global.IsNil() { if !global.IsNil() {
// method set already exist, return it // method set already exist, return it
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{zero, zero}) return llvm.ConstGEP(global, []llvm.Value{zero, zero})
} }
// Every method is a *i8 reference indicating the signature of this method. // Every method is a *i8 reference indicating the signature of this method.
@@ -303,7 +303,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
global.SetInitializer(value) global.SetInitializer(value)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage) global.SetLinkage(llvm.LinkOnceODRLinkage)
return llvm.ConstGEP(value.Type(), global, []llvm.Value{zero, zero}) return llvm.ConstGEP(global, []llvm.Value{zero, zero})
} }
// getMethodSignatureName returns a unique name (that can be used as the name of // getMethodSignatureName returns a unique name (that can be used as the name of
@@ -361,7 +361,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// implements each method of the interface. See: // implements each method of the interface. See:
// https://research.swtch.com/interfaces // https://research.swtch.com/interfaces
fn := b.getInterfaceImplementsFunc(expr.AssertedType) fn := b.getInterfaceImplementsFunc(expr.AssertedType)
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "") commaOk = b.CreateCall(fn, []llvm.Value{actualTypeNum}, "")
} else { } else {
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType) globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
@@ -465,7 +465,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
paramTuple = append(paramTuple, sig.Params().At(i)) paramTuple = append(paramTuple, sig.Params().At(i))
} }
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr])) paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr]))
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)) llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)).ElementType()
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn) c.addStandardDeclaredAttributes(llvmFn)
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method))) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
@@ -480,7 +480,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
// value, dereferences or unpacks it if necessary, and calls the real method. // value, dereferences or unpacks it if necessary, and calls the real method.
// If the method to wrap has a pointer receiver, no wrapping is necessary and // If the method to wrap has a pointer receiver, no wrapping is necessary and
// the function is returned directly. // the function is returned directly.
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType llvm.Type, llvmFn llvm.Value) llvm.Value { func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llvm.Value) llvm.Value {
wrapperName := llvmFn.Name() + "$invoke" wrapperName := llvmFn.Name() + "$invoke"
wrapper := c.mod.NamedFunction(wrapperName) wrapper := c.mod.NamedFunction(wrapperName)
if !wrapper.IsNil() { if !wrapper.IsNil() {
@@ -505,8 +505,9 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
} }
// create wrapper function // create wrapper function
paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...) fnType := llvmFn.Type().ElementType()
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false) paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType) wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
c.addStandardAttributes(wrapper) c.addStandardAttributes(wrapper)
@@ -533,11 +534,11 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...) params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
if llvmFnType.ReturnType().TypeKind() == llvm.VoidTypeKind { if llvmFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
b.CreateCall(llvmFnType, llvmFn, params, "") b.CreateCall(llvmFn, params, "")
b.CreateRetVoid() b.CreateRetVoid()
} else { } else {
ret := b.CreateCall(llvmFnType, llvmFn, params, "ret") ret := b.CreateCall(llvmFn, params, "ret")
b.CreateRet(ret) b.CreateRet(ret)
} }
+5 -7
View File
@@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Fall back to a generic error. // Fall back to a generic error.
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
} }
_, funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil) funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType) funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
// Create a new global of type runtime/interrupt.handle. Globals of this // Create a new global of type runtime/interrupt.handle. Globals of this
@@ -49,11 +49,9 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
initializer := llvm.ConstNull(globalLLVMType) initializer := llvm.ConstNull(globalLLVMType)
initializer = b.CreateInsertValue(initializer, funcContext, 0, "") initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "") initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1})
initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{ initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0})
llvm.ConstInt(b.intType, uint64(id.Int64()), true),
}), 2, "")
global.SetInitializer(initializer) global.SetInitializer(initializer)
// Add debug info to the interrupt global. // Add debug info to the interrupt global.
@@ -87,7 +85,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType) useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
} }
b.CreateCall(useFn.GlobalValueType(), useFn, []llvm.Value{interrupt}, "") b.CreateCall(useFn, []llvm.Value{interrupt}, "")
} }
return interrupt, nil return interrupt, nil
+5 -12
View File
@@ -7,7 +7,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -45,10 +44,7 @@ func (b *builder) defineIntrinsicFunction() {
// and will otherwise be lowered to regular libc memcpy/memmove calls. // and will otherwise be lowered to regular libc memcpy/memmove calls.
func (b *builder) createMemoryCopyImpl() { func (b *builder) createMemoryCopyImpl() {
b.createFunctionStart(true) b.createFunctionStart(true)
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) fnName := "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
}
llvmFn := b.mod.NamedFunction(fnName) llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
@@ -59,7 +55,7 @@ func (b *builder) createMemoryCopyImpl() {
params = append(params, b.getValue(param)) params = append(params, b.getValue(param))
} }
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false)) params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "") b.CreateCall(llvmFn, params, "")
b.CreateRetVoid() b.CreateRetVoid()
} }
@@ -68,10 +64,7 @@ func (b *builder) createMemoryCopyImpl() {
// regular libc memset calls if they aren't optimized out in a different way. // regular libc memset calls if they aren't optimized out in a different way.
func (b *builder) createMemoryZeroImpl() { func (b *builder) createMemoryZeroImpl() {
b.createFunctionStart(true) b.createFunctionStart(true)
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
}
llvmFn := b.mod.NamedFunction(fnName) llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
@@ -83,7 +76,7 @@ func (b *builder) createMemoryZeroImpl() {
b.getValue(b.fn.Params[1]), b.getValue(b.fn.Params[1]),
llvm.ConstInt(b.ctx.Int1Type(), 0, false), llvm.ConstInt(b.ctx.Int1Type(), 0, false),
} }
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "") b.CreateCall(llvmFn, params, "")
b.CreateRetVoid() b.CreateRetVoid()
} }
@@ -126,6 +119,6 @@ func (b *builder) defineMathOp() {
for i, param := range b.fn.Params { for i, param := range b.fn.Params {
args[i] = b.getValue(param) args[i] = b.getValue(param)
} }
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "") result := b.CreateCall(llvmFn, args, "")
b.CreateRet(result) b.CreateRet(result)
} }
+4 -1
View File
@@ -70,7 +70,10 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
return fmt.Errorf("failed to verify element type of array type %s: %s", t.String(), err.Error()) return fmt.Errorf("failed to verify element type of array type %s: %s", t.String(), err.Error())
} }
case llvm.PointerTypeKind: case llvm.PointerTypeKind:
// Pointers can't be checked in an opaque pointer world. // check underlying type
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
return fmt.Errorf("failed to verify underlying type of pointer type %s: %s", t.String(), err.Error())
}
case llvm.VectorTypeKind: case llvm.VectorTypeKind:
// check element type // check element type
if err := c.checkType(t.ElementType(), checked, specials); err != nil { if err := c.checkType(t.ElementType(), checked, specials); err != nil {
+7 -165
View File
@@ -51,175 +51,29 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
// emitPointerPack packs the list of values into a single pointer value using // emitPointerPack packs the list of values into a single pointer value using
// bitcasts, or else allocates a value on the heap if it cannot be packed in the // bitcasts, or else allocates a value on the heap if it cannot be packed in the
// pointer value directly. It returns the pointer with the packed data. // pointer value directly. It returns the pointer with the packed data.
// If the values are all constants, they are be stored in a constant global and
// deduplicated.
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
valueTypes := make([]llvm.Type, len(values)) return llvmutil.EmitPointerPack(b.Builder, b.mod, b.pkg.Path(), b.NeedsStackObjects, values)
for i, value := range values {
valueTypes[i] = value.Type()
}
packedType := b.ctx.StructType(valueTypes, false)
// Allocate memory for the packed data.
size := b.targetData.TypeAllocSize(packedType)
if size == 0 {
return llvm.ConstPointerNull(b.i8ptrType)
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
return b.CreateBitCast(values[0], b.i8ptrType, "pack.ptr")
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
// Packed data fits in a pointer, so store it directly inside the
// pointer.
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
// Try to keep this cast in SSA form.
return b.CreateIntToPtr(values[0], b.i8ptrType, "pack.int")
}
// Because packedType is a struct and we have to cast it to a *i8, store
// it in a *i8 alloca first and load the *i8 value from there. This is
// effectively a bitcast.
packedAlloc, _, _ := b.createTemporaryAlloca(b.i8ptrType, "")
if size < b.targetData.TypeAllocSize(b.i8ptrType) {
// The alloca is bigger than the value that will be stored in it.
// To avoid having some bits undefined, zero the alloca first.
// Hopefully this will get optimized away.
b.CreateStore(llvm.ConstNull(b.i8ptrType), packedAlloc)
}
// Store all values in the alloca.
packedAllocCast := b.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}
gep := b.CreateInBoundsGEP(packedType, packedAllocCast, indices, "")
b.CreateStore(value, gep)
}
// Load value (the *i8) from the alloca.
result := b.CreateLoad(b.i8ptrType, packedAlloc, "")
// End the lifetime of the alloca, to help the optimizer.
packedPtr := b.CreateBitCast(packedAlloc, b.i8ptrType, "")
packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false)
b.emitLifetimeEnd(packedPtr, packedSize)
return result
} else {
// Check if the values are all constants.
constant := true
for _, v := range values {
if !v.IsConstant() {
constant = false
break
}
}
if constant {
// The data is known at compile time, so store it in a constant global.
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
global := llvm.AddGlobal(b.mod, packedType, b.pkg.Path()+"$pack")
global.SetInitializer(b.ctx.ConstStruct(values, false))
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetLinkage(llvm.InternalLinkage)
return llvm.ConstBitCast(global, b.i8ptrType)
}
// Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
alloc := b.mod.NamedFunction("runtime.alloc")
packedHeapAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
sizeValue,
llvm.ConstNull(b.i8ptrType),
llvm.Undef(b.i8ptrType), // unused context parameter
}, "")
if b.NeedsStackObjects {
b.trackPointer(packedHeapAlloc)
}
packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
// Store all values in the heap pointer.
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
b.CreateStore(value, gep)
}
// Return the original heap allocation pointer, which already is an *i8.
return packedHeapAlloc
}
} }
// emitPointerUnpack extracts a list of values packed using emitPointerPack. // emitPointerUnpack extracts a list of values packed using emitPointerPack.
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value { func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
packedType := b.ctx.StructType(valueTypes, false) return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes)
// Get a correctly-typed pointer to the packed data.
var packedAlloc, packedRawAlloc llvm.Value
size := b.targetData.TypeAllocSize(packedType)
if size == 0 {
// No data to unpack.
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
// A single pointer is always stored directly.
return []llvm.Value{b.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
// Packed data stored directly in pointer.
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
// Keep this cast in SSA form.
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
}
// Fallback: load it using an alloca.
packedRawAlloc, _, _ = b.createTemporaryAlloca(llvm.PointerType(b.i8ptrType, 0), "unpack.raw.alloc")
packedRawValue := b.CreateBitCast(ptr, llvm.PointerType(b.i8ptrType, 0), "unpack.raw.value")
b.CreateStore(packedRawValue, packedRawAlloc)
packedAlloc = b.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
} else {
// Packed data stored on the heap. Bitcast the passed-in pointer to the
// correct pointer type.
packedAlloc = b.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
}
// Load each value from the packed data.
values := make([]llvm.Value, len(valueTypes))
for i, valueType := range valueTypes {
if b.targetData.TypeAllocSize(valueType) == 0 {
// This value has length zero, so there's nothing to load.
values[i] = llvm.ConstNull(valueType)
continue
}
indices := []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
values[i] = b.CreateLoad(valueType, gep, "")
}
if !packedRawAlloc.IsNil() {
allocPtr := b.CreateBitCast(packedRawAlloc, b.i8ptrType, "")
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
b.emitLifetimeEnd(allocPtr, allocSize)
}
return values
} }
// makeGlobalArray creates a new LLVM global with the given name and integers as // makeGlobalArray creates a new LLVM global with the given name and integers as
// contents, and returns the global and initializer type. // contents, and returns the global.
// Note that it is left with the default linkage etc., you should set // Note that it is left with the default linkage etc., you should set
// linkage/constant/etc properties yourself. // linkage/constant/etc properties yourself.
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) (llvm.Type, llvm.Value) { func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value {
globalType := llvm.ArrayType(elementType, len(buf)) globalType := llvm.ArrayType(elementType, len(buf))
global := llvm.AddGlobal(c.mod, globalType, name) global := llvm.AddGlobal(c.mod, globalType, name)
value := llvm.Undef(globalType) value := llvm.Undef(globalType)
for i := 0; i < len(buf); i++ { for i := 0; i < len(buf); i++ {
ch := uint64(buf[i]) ch := uint64(buf[i])
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "") value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
} }
global.SetInitializer(value) global.SetInitializer(value)
return globalType, global return global
} }
// createObjectLayout returns a LLVM value (of type i8*) that describes where // createObjectLayout returns a LLVM value (of type i8*) that describes where
@@ -230,8 +84,6 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
// which words contain a pointer (indicated by setting the given bit to 1). For // which words contain a pointer (indicated by setting the given bit to 1). For
// arrays, only the element is stored. This works because the GC knows the // arrays, only the element is stored. This works because the GC knows the
// object size and can therefore know how this value is repeated in the object. // object size and can therefore know how this value is repeated in the object.
//
// For details on what's in this value, see src/runtime/gc_precise.go.
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value { func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
// Use the element type for arrays. This works even for nested arrays. // Use the element type for arrays. This works even for nested arrays.
for { for {
@@ -314,7 +166,6 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// Create the global initializer. // Create the global initializer.
bitmapBytes := make([]byte, int(objectSizeWords+7)/8) bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
bitmap.FillBytes(bitmapBytes) bitmap.FillBytes(bitmapBytes)
reverseBytes(bitmapBytes) // big-endian to little-endian
var bitmapByteValues []llvm.Value var bitmapByteValues []llvm.Value
for _, b := range bitmapBytes { for _, b := range bitmapBytes {
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false)) bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
@@ -461,14 +312,5 @@ func (b *builder) readStackPointer() llvm.Value {
fnType := llvm.FunctionType(b.i8ptrType, nil, false) fnType := llvm.FunctionType(b.i8ptrType, nil, false)
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType) stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
} }
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "") return b.CreateCall(stacksave, nil, "")
}
// Reverse a slice of bytes. From the wiki:
// https://github.com/golang/go/wiki/SliceTricks#reversing
func reverseBytes(buf []byte) {
for i := len(buf)/2 - 1; i >= 0; i-- {
opp := len(buf) - 1 - i
buf[i], buf[opp] = buf[opp], buf[i]
}
} }
+15 -71
View File
@@ -7,22 +7,7 @@
// places would be a big risk if only one of them is updated. // places would be a big risk if only one of them is updated.
package llvmutil package llvmutil
import ( import "tinygo.org/x/go-llvm"
"strconv"
"strings"
"tinygo.org/x/go-llvm"
)
// Major returns the LLVM major version.
func Major() int {
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil {
// sanity check, should be unreachable
panic("could not parse LLVM version: " + err.Error())
}
return llvmMajor
}
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though // CreateEntryBlockAlloca creates a new alloca in the entry block, even though
// the IR builder is located elsewhere. It assumes that the insert point is // the IR builder is located elsewhere. It assumes that the insert point is
@@ -54,8 +39,7 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
alloca = CreateEntryBlockAlloca(builder, t, name) alloca = CreateEntryBlockAlloca(builder, t, name)
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast") bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod) builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
return return
} }
@@ -70,15 +54,13 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
builder.SetInsertPointBefore(inst) builder.SetInsertPointBefore(inst)
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast") bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod) builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
if next := llvm.NextInstruction(inst); !next.IsNil() { if next := llvm.NextInstruction(inst); !next.IsNil() {
builder.SetInsertPointBefore(next) builder.SetInsertPointBefore(next)
} else { } else {
builder.SetInsertPointAtEnd(inst.InstructionParent()) builder.SetInsertPointAtEnd(inst.InstructionParent())
} }
fnType, fn = getLifetimeEndFunc(mod) builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, bitcast}, "")
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
return alloca return alloca
} }
@@ -86,42 +68,33 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
// llvm.lifetime.end intrinsic. It is commonly used together with // llvm.lifetime.end intrinsic. It is commonly used together with
// createTemporaryAlloca. // createTemporaryAlloca.
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) { func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
fnType, fn := getLifetimeEndFunc(mod) builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, ptr}, "")
builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "")
} }
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it // getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
// first if it doesn't exist yet. // first if it doesn't exist yet.
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) { func getLifetimeStartFunc(mod llvm.Module) llvm.Value {
fnName := "llvm.lifetime.start.p0" fn := mod.NamedFunction("llvm.lifetime.start.p0i8")
if Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.lifetime.start.p0i8"
}
fn := mod.NamedFunction(fnName)
ctx := mod.Context() ctx := mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
if fn.IsNil() { if fn.IsNil() {
fn = llvm.AddFunction(mod, fnName, fnType) fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
fn = llvm.AddFunction(mod, "llvm.lifetime.start.p0i8", fnType)
} }
return fnType, fn return fn
} }
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it // getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
// first if it doesn't exist yet. // first if it doesn't exist yet.
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) { func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
fnName := "llvm.lifetime.end.p0" fn := mod.NamedFunction("llvm.lifetime.end.p0i8")
if Major() < 15 {
fnName = "llvm.lifetime.end.p0i8"
}
fn := mod.NamedFunction(fnName)
ctx := mod.Context() ctx := mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
if fn.IsNil() { if fn.IsNil() {
fn = llvm.AddFunction(mod, fnName, fnType) fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
fn = llvm.AddFunction(mod, "llvm.lifetime.end.p0i8", fnType)
} }
return fnType, fn return fn
} }
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions // SplitBasicBlock splits a LLVM basic block into two parts. All instructions
@@ -195,32 +168,3 @@ func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llv
return newBlock return newBlock
} }
// Append the given values to a global array like llvm.used. The global might
// not exist yet. The values can be any pointer type, they will be cast to i8*.
func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
// Read the existing values in the llvm.used array (if it exists).
var usedValues []llvm.Value
if used := mod.NamedGlobal(globalName); !used.IsNil() {
builder := mod.Context().NewBuilder()
defer builder.Dispose()
usedInitializer := used.Initializer()
num := usedInitializer.Type().ArrayLength()
for i := 0; i < num; i++ {
usedValues = append(usedValues, builder.CreateExtractValue(usedInitializer, i, ""))
}
used.EraseFromParentAsGlobal()
}
// Add the new values.
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
for _, value := range values {
usedValues = append(usedValues, llvm.ConstPointerCast(value, i8ptrType))
}
// Create a new array (with the old and new values).
usedInitializer := llvm.ConstArray(i8ptrType, usedValues)
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
used.SetInitializer(usedInitializer)
used.SetLinkage(llvm.AppendingLinkage)
}
+182
View File
@@ -0,0 +1,182 @@
package llvmutil
// This file contains utility functions to pack and unpack sets of values. It
// can take in a list of values and tries to store it efficiently in the pointer
// itself if possible and legal.
import (
"tinygo.org/x/go-llvm"
)
// EmitPointerPack packs the list of values into a single pointer value using
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
// pointer value directly. It returns the pointer with the packed data.
// If the values are all constants, they are be stored in a constant global and deduplicated.
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(targetData.PointerSize() * 8)
valueTypes := make([]llvm.Type, len(values))
for i, value := range values {
valueTypes[i] = value.Type()
}
packedType := ctx.StructType(valueTypes, false)
// Allocate memory for the packed data.
size := targetData.TypeAllocSize(packedType)
if size == 0 {
return llvm.ConstPointerNull(i8ptrType)
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
return builder.CreateBitCast(values[0], i8ptrType, "pack.ptr")
} else if size <= targetData.TypeAllocSize(i8ptrType) {
// Packed data fits in a pointer, so store it directly inside the
// pointer.
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
// Try to keep this cast in SSA form.
return builder.CreateIntToPtr(values[0], i8ptrType, "pack.int")
}
// Because packedType is a struct and we have to cast it to a *i8, store
// it in a *i8 alloca first and load the *i8 value from there. This is
// effectively a bitcast.
packedAlloc, _, _ := CreateTemporaryAlloca(builder, mod, i8ptrType, "")
if size < targetData.TypeAllocSize(i8ptrType) {
// The alloca is bigger than the value that will be stored in it.
// To avoid having some bits undefined, zero the alloca first.
// Hopefully this will get optimized away.
builder.CreateStore(llvm.ConstNull(i8ptrType), packedAlloc)
}
// Store all values in the alloca.
packedAllocCast := builder.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(ctx.Int32Type(), 0, false),
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
}
gep := builder.CreateInBoundsGEP(packedAllocCast, indices, "")
builder.CreateStore(value, gep)
}
// Load value (the *i8) from the alloca.
result := builder.CreateLoad(packedAlloc, "")
// End the lifetime of the alloca, to help the optimizer.
packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
return result
} else {
// Check if the values are all constants.
constant := true
for _, v := range values {
if !v.IsConstant() {
constant = false
break
}
}
if constant {
// The data is known at compile time, so store it in a constant global.
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
global := llvm.AddGlobal(mod, packedType, prefix+"$pack")
global.SetInitializer(ctx.ConstStruct(values, false))
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetLinkage(llvm.InternalLinkage)
return llvm.ConstBitCast(global, i8ptrType)
}
// Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(uintptrType, size, false)
alloc := mod.NamedFunction("runtime.alloc")
packedHeapAlloc := builder.CreateCall(alloc, []llvm.Value{
sizeValue,
llvm.ConstNull(i8ptrType),
llvm.Undef(i8ptrType), // unused context parameter
}, "")
if needsStackObjects {
trackPointer := mod.NamedFunction("runtime.trackPointer")
builder.CreateCall(trackPointer, []llvm.Value{
packedHeapAlloc,
llvm.Undef(i8ptrType), // unused context parameter
}, "")
}
packedAlloc := builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
// Store all values in the heap pointer.
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(ctx.Int32Type(), 0, false),
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
}
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
builder.CreateStore(value, gep)
}
// Return the original heap allocation pointer, which already is an *i8.
return packedHeapAlloc
}
}
// EmitPointerUnpack extracts a list of values packed using EmitPointerPack.
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(targetData.PointerSize() * 8)
packedType := ctx.StructType(valueTypes, false)
// Get a correctly-typed pointer to the packed data.
var packedAlloc, packedRawAlloc llvm.Value
size := targetData.TypeAllocSize(packedType)
if size == 0 {
// No data to unpack.
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
// A single pointer is always stored directly.
return []llvm.Value{builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
} else if size <= targetData.TypeAllocSize(i8ptrType) {
// Packed data stored directly in pointer.
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
// Keep this cast in SSA form.
return []llvm.Value{builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
}
// Fallback: load it using an alloca.
packedRawAlloc, _, _ = CreateTemporaryAlloca(builder, mod, llvm.PointerType(i8ptrType, 0), "unpack.raw.alloc")
packedRawValue := builder.CreateBitCast(ptr, llvm.PointerType(i8ptrType, 0), "unpack.raw.value")
builder.CreateStore(packedRawValue, packedRawAlloc)
packedAlloc = builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
} else {
// Packed data stored on the heap. Bitcast the passed-in pointer to the
// correct pointer type.
packedAlloc = builder.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
}
// Load each value from the packed data.
values := make([]llvm.Value, len(valueTypes))
for i, valueType := range valueTypes {
if targetData.TypeAllocSize(valueType) == 0 {
// This value has length zero, so there's nothing to load.
values[i] = llvm.ConstNull(valueType)
continue
}
indices := []llvm.Value{
llvm.ConstInt(ctx.Int32Type(), 0, false),
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
}
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
values[i] = builder.CreateLoad(gep, "")
}
if !packedRawAlloc.IsNil() {
allocPtr := builder.CreateBitCast(packedRawAlloc, i8ptrType, "")
allocSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(uintptrType), false)
EmitLifetimeEnd(builder, mod, allocPtr, allocSize)
}
return values
}
+5 -5
View File
@@ -41,8 +41,8 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
} }
keySize := b.targetData.TypeAllocSize(llvmKeyType) keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType) valueSize := b.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false) llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false) llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
sizeHint := llvm.ConstInt(b.uintptrType, 8, false) sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false) algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
if expr.Reserve != nil { if expr.Reserve != nil {
@@ -106,7 +106,7 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// Load the resulting value from the hashmap. The value is set to the zero // Load the resulting value from the hashmap. The value is set to the zero
// value if the key doesn't exist in the hashmap. // value if the key doesn't exist in the hashmap.
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "") mapValue := b.CreateLoad(mapValueAlloca, "")
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize) b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
if commaOk { if commaOk {
@@ -217,8 +217,8 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key") mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value") mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next") ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "") mapKey := b.CreateLoad(mapKeyAlloca, "")
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "") mapValue := b.CreateLoad(mapValueAlloca, "")
if isKeyStoredAsInterface { if isKeyStoredAsInterface {
// The key is stored as an interface but it isn't of interface type. // The key is stored as an interface but it isn't of interface type.
+16 -29
View File
@@ -10,7 +10,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/loader" "github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
@@ -54,11 +53,11 @@ const (
// getFunction returns the LLVM function for the given *ssa.Function, creating // getFunction returns the LLVM function for the given *ssa.Function, creating
// it if needed. It can later be filled with compilerContext.createFunction(). // it if needed. It can later be filled with compilerContext.createFunction().
func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) { func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
info := c.getFunctionInfo(fn) info := c.getFunctionInfo(fn)
llvmFn := c.mod.NamedFunction(info.linkName) llvmFn := c.mod.NamedFunction(info.linkName)
if !llvmFn.IsNil() { if !llvmFn.IsNil() {
return llvmFn.GlobalValueType(), llvmFn return llvmFn
} }
var retType llvm.Type var retType llvm.Type
@@ -84,7 +83,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// Add an extra parameter as the function context. This context is used in // Add an extra parameter as the function context. This context is used in
// closures and bound methods, but should be optimized away when not used. // closures and bound methods, but should be optimized away when not used.
if !info.exported { if !info.exported {
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", elemSize: 0}) paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
} }
var paramTypes []llvm.Type var paramTypes []llvm.Type
@@ -113,8 +112,17 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null") dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
for i, info := range paramInfos { for i, info := range paramInfos {
if info.elemSize != 0 { if info.flags&paramIsDeferenceableOrNull == 0 {
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize) continue
}
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
el := info.llvmType.ElementType()
size := c.targetData.TypeAllocSize(el)
if size == 0 {
// dereferenceable_or_null(0) appears to be illegal in LLVM.
continue
}
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, size)
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull) llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
} }
} }
@@ -150,19 +158,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// that the only thing we'll do is read the pointer. // that the only thing we'll do is read the pointer.
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "__mulsi3", "__divmodsi4", "__udivmodsi4":
if strings.Split(c.Triple, "-")[0] == "avr" {
// These functions are compiler-rt/libgcc functions that are
// currently implemented in Go. Assembly versions should appear in
// LLVM 16 hopefully. Until then, they need to be made available to
// the linker and the best way to do that is llvm.compiler.used.
// I considered adding a pragma for this, but the LLVM language
// reference explicitly says that this feature should not be exposed
// to source languages:
// > This is a rare construct that should only be used in rare
// > circumstances, and should not be exposed to source languages.
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
}
} }
// External/exported functions may not retain pointer values. // External/exported functions may not retain pointer values.
@@ -207,7 +202,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
llvmFn.SetUnnamedAddr(true) llvmFn.SetUnnamedAddr(true)
} }
return fnType, llvmFn return llvmFn
} }
// getFunctionInfo returns information about a function that is not directly // getFunctionInfo returns information about a function that is not directly
@@ -367,15 +362,7 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0)) llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
if strings.Split(c.Triple, "-")[0] == "x86_64" { if strings.Split(c.Triple, "-")[0] == "x86_64" {
// Required by the ABI. // Required by the ABI.
if llvmutil.Major() < 15 { llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
// Needed for LLVM 14 support.
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
} else {
// The uwtable has two possible values: sync (1) or async (2). We
// use sync because we currently don't use async unwind tables.
// For details, see: https://llvm.org/docs/LangRef.html#function-attributes
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
}
} }
} }
+7 -7
View File
@@ -44,7 +44,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
constraints += ",~{rcx},~{r11}" constraints += ",~{rcx},~{r11}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(target, args, ""), nil
case b.GOARCH == "386" && b.GOOS == "linux": case b.GOARCH == "386" && b.GOOS == "linux":
// Sources: // Sources:
// syscall(2) man page // syscall(2) man page
@@ -70,7 +70,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
} }
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false) target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(target, args, ""), nil
case b.GOARCH == "arm" && b.GOOS == "linux": case b.GOARCH == "arm" && b.GOOS == "linux":
// Implement the EABI system call convention for Linux. // Implement the EABI system call convention for Linux.
// Source: syscall(2) man page. // Source: syscall(2) man page.
@@ -102,7 +102,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
} }
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(target, args, ""), nil
case b.GOARCH == "arm64" && b.GOOS == "linux": case b.GOARCH == "arm64" && b.GOOS == "linux":
// Source: syscall(2) man page. // Source: syscall(2) man page.
args := []llvm.Value{} args := []llvm.Value{}
@@ -134,7 +134,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
constraints += ",~{x16},~{x17}" // scratch registers constraints += ",~{x16},~{x17}" // scratch registers
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(target, args, ""), nil
default: default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH) return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
} }
@@ -205,9 +205,9 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
// Note that SetLastError/GetLastError could be replaced with direct // Note that SetLastError/GetLastError could be replaced with direct
// access to the thread control block, which is probably smaller and // access to the thread control block, which is probably smaller and
// faster. The Go runtime does this in assembly. // faster. The Go runtime does this in assembly.
b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "") b.CreateCall(setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
syscallResult := b.CreateCall(llvmType, fnPtr, params, "") syscallResult := b.CreateCall(fnPtr, params, "")
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err") errResult := b.CreateCall(getLastError, nil, "err")
if b.uintptrType != b.ctx.Int32Type() { if b.uintptrType != b.ctx.Int32Type() {
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr") errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
} }
+4 -14
View File
@@ -75,24 +75,14 @@ func complexMul(x, y complex64) complex64 {
// A type 'kv' also exists in function foo. Test that these two types don't // A type 'kv' also exists in function foo. Test that these two types don't
// conflict with each other. // conflict with each other.
type kv struct { type kv struct {
v float32 v float32
x, y, z int
} }
var kvGlobal kv func foo(a *kv) {
func foo() {
// Define a new 'kv' type. // Define a new 'kv' type.
type kv struct { type kv struct {
v byte v byte
x, y, z int
} }
// Use this type. // Use this type.
func(b kv) {}(kv{}) func(b *kv) {}(nil)
} }
type T1 []T1
type T2 [2]*T2
var a T1
var b T2
+30 -43
View File
@@ -3,39 +3,35 @@ source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%main.kv = type { float, i32, i32, i32 } %main.kv = type { float }
%main.kv.0 = type { i8, i32, i32, i32 } %main.kv.0 = type { i8 }
@main.kvGlobal = hidden global %main.kv zeroinitializer, align 4 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = add i32 %x, %y %0 = add i32 %x, %y
ret i32 %0 ret i32 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = icmp eq i32 %x, %y %0 = icmp eq i32 %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = icmp eq i32 %y, 0 %0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -49,14 +45,14 @@ divbyzero.next: ; preds = %entry
ret i32 %5 ret i32 %5
divbyzero.throw: ; preds = %entry divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2 call void @runtime.divideByZeroPanic(i8* undef) #2
unreachable unreachable
} }
declare void @runtime.divideByZeroPanic(ptr) #0 declare void @runtime.divideByZeroPanic(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = icmp eq i32 %y, 0 %0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -66,12 +62,12 @@ divbyzero.next: ; preds = %entry
ret i32 %1 ret i32 %1
divbyzero.throw: ; preds = %entry divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2 call void @runtime.divideByZeroPanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = icmp eq i32 %y, 0 %0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -85,12 +81,12 @@ divbyzero.next: ; preds = %entry
ret i32 %5 ret i32 %5
divbyzero.throw: ; preds = %entry divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2 call void @runtime.divideByZeroPanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = icmp eq i32 %y, 0 %0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -100,66 +96,66 @@ divbyzero.next: ; preds = %entry
ret i32 %1 ret i32 %1
divbyzero.throw: ; preds = %entry divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2 call void @runtime.divideByZeroPanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.floatEQ(float %x, float %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fcmp oeq float %x, %y %0 = fcmp oeq float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.floatNE(float %x, float %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fcmp une float %x, %y %0 = fcmp une float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.floatLower(float %x, float %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fcmp olt float %x, %y %0 = fcmp olt float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fcmp ole float %x, %y %0 = fcmp ole float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.floatGreater(float %x, float %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fcmp ogt float %x, %y %0 = fcmp ogt float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fcmp oge float %x, %y %0 = fcmp oge float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 { define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context) unnamed_addr #1 {
entry: entry:
ret float %x.r ret float %x.r
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #1 { define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context) unnamed_addr #1 {
entry: entry:
ret float %x.i ret float %x.i
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 { define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fadd float %x.r, %y.r %0 = fadd float %x.r, %y.r
%1 = fadd float %x.i, %y.i %1 = fadd float %x.i, %y.i
@@ -169,7 +165,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 { define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fsub float %x.r, %y.r %0 = fsub float %x.r, %y.r
%1 = fsub float %x.i, %y.i %1 = fsub float %x.i, %y.i
@@ -179,7 +175,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 { define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = fmul float %x.r, %y.r %0 = fmul float %x.r, %y.r
%1 = fmul float %x.i, %y.i %1 = fmul float %x.i, %y.i
@@ -193,24 +189,15 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.foo(ptr %context) unnamed_addr #1 { define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #1 {
entry: entry:
%complit = alloca %main.kv.0, align 8 call void @"main.foo$1"(%main.kv.0* null, i8* undef)
%stackalloc = alloca i8, align 1
store %main.kv.0 zeroinitializer, ptr %complit, align 8
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #2
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 { define internal void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #1 {
entry: entry:
%b1 = alloca %main.kv.0, align 8
%stackalloc = alloca i8, align 1
store %main.kv.0 zeroinitializer, ptr %b1, align 8
call void @runtime.trackPointer(ptr nonnull %b1, ptr nonnull %stackalloc, ptr undef) #2
store %main.kv.0 %b, ptr %b1, align 8
ret void ret void
} }
+63 -48
View File
@@ -3,95 +3,110 @@ source_filename = "channel.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.channelBlockedList = type { ptr, ptr, ptr, { ptr, i32, i32 } } %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.chanSelectState = type { ptr, ptr } %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", 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 ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
entry: entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4 %chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value) %chan.value.bitcast = bitcast i32* %chan.value to i8*
store i32 3, ptr %chan.value, align 4 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) store i32 3, i32* %chan.value, align 4
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3 %chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList) call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value) 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 ret void
} }
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn ; Function Attrs: argmemonly nofree nosync nounwind willreturn
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #0 declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn ; Function Attrs: argmemonly nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2 declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
entry: entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4 %chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value) %chan.value.bitcast = bitcast i32* %chan.value to i8*
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3 %chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value) call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList) %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 ret void
} }
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #0 declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
entry: entry:
%complit = alloca {}, align 8 %complit = alloca {}, align 8
%chan.blockedList = alloca %runtime.channelBlockedList, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
%stackalloc = alloca i8, align 1 %0 = bitcast {}* %complit to i8*
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #3
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) %chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3 call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList) 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 ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
entry: entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) %chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3 call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList) %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 ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(32) %ch1, ptr dereferenceable_or_null(32) %ch2, ptr %context) unnamed_addr #1 { 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: entry:
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8 %select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
%select.send.value = alloca i32, align 4 %select.send.value = alloca i32, align 4
store i32 1, ptr %select.send.value, align 4 store i32 1, i32* %select.send.value, align 4
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca) %select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
store ptr %ch1, ptr %select.states.alloca, align 8 call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
%select.states.alloca.repack1 = getelementptr inbounds %runtime.chanSelectState, ptr %select.states.alloca, i32 0, i32 1 %.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4 store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
%0 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1 %.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
store ptr %ch2, ptr %0, align 8 %0 = bitcast i8** %.repack1 to i32**
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1, i32 1 store i32* %select.send.value, i32** %0, align 4
store ptr null, ptr %.repack3, align 4 %.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
%select.result = call { i32, i1 } @runtime.tryChanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr undef) #3 store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca) %.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) #3
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
%1 = extractvalue { i32, i1 } %select.result, 0 %1 = extractvalue { i32, i1 } %select.result, 0
%2 = icmp eq i32 %1, 0 %2 = icmp eq i32 %1, 0
br i1 %2, label %select.done, label %select.next br i1 %2, label %select.done, label %select.next
@@ -107,9 +122,9 @@ select.body: ; preds = %select.next
br label %select.done br label %select.done
} }
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #0 declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*) #0
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { argmemonly nocallback nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+138 -127
View File
@@ -3,99 +3,103 @@ source_filename = "defer.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" 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" target triple = "thumbv7m-unknown-unknown-eabi"
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface } %runtime._defer = type { i32, %runtime._defer* }
%runtime._interface = type { i32, ptr } %runtime.deferFrame = type { i8*, i8*, [0 x i8*], %runtime.deferFrame*, i1, %runtime._interface }
%runtime._defer = type { i32, ptr } %runtime._interface = type { i32, i8* }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
declare void @main.external(ptr) #0 declare void @main.external(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 { define hidden void @main.deferSimple(i8* %context) unnamed_addr #1 {
entry: entry:
%defer.alloca = alloca { i32, ptr }, align 4 %defer.alloca = alloca { i32, %runtime._defer* }, align 4
%deferPtr = alloca ptr, align 4 %deferPtr = alloca %runtime._defer*, align 4
store ptr null, ptr %deferPtr, align 4 store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4 %deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave() %0 = call i8* @llvm.stacksave()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3 call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
store i32 0, ptr %defer.alloca, align 4 %defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
%defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1 store i32 0, i32* %defer.alloca.repack, align 4
store ptr null, ptr %defer.alloca.repack15, align 4 %defer.alloca.repack16 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
store ptr %defer.alloca, ptr %deferPtr, align 4 store %runtime._defer* null, %runtime._defer** %defer.alloca.repack16, align 4
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #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 %setjmp.result = icmp eq i32 %setjmp, 0
br i1 %setjmp.result, label %1, label %lpad br i1 %setjmp.result, label %2, label %lpad
1: ; preds = %entry 2: ; preds = %entry
call void @main.external(ptr undef) #3 call void @main.external(i8* undef) #3
br label %rundefers.loophead br label %rundefers.loophead
rundefers.loophead: ; preds = %3, %1 rundefers.loophead: ; preds = %4, %2
%2 = load ptr, ptr %deferPtr, align 4 %3 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
%stackIsNil = icmp eq ptr %2, null %stackIsNil = icmp eq %runtime._defer* %3, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1 %stack.next.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %3, i32 0, i32 1
%stack.next = load ptr, ptr %stack.next.gep, align 4 %stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4 store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
%callback = load i32, ptr %2, 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 [ switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0 i32 0, label %rundefers.callback0
] ]
rundefers.callback0: ; preds = %rundefers.loop 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}"(ptr nonnull %deferframe.buf) #4 %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 %setjmp.result2 = icmp eq i32 %setjmp1, 0
br i1 %setjmp.result2, label %3, label %lpad br i1 %setjmp.result2, label %4, label %lpad
3: ; preds = %rundefers.callback0 4: ; preds = %rundefers.callback0
call void @"main.deferSimple$1"(ptr undef) call void @"main.deferSimple$1"(i8* undef)
br label %rundefers.loophead br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop rundefers.default: ; preds = %rundefers.loop
unreachable unreachable
rundefers.end: ; preds = %rundefers.loophead rundefers.end: ; preds = %rundefers.loophead
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3 call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
ret void ret void
recover: ; preds = %rundefers.end3 recover: ; preds = %rundefers.end3
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3 call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
ret void ret void
lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry
br label %rundefers.loophead6 br label %rundefers.loophead6
rundefers.loophead6: ; preds = %5, %lpad rundefers.loophead6: ; preds = %6, %lpad
%4 = load ptr, ptr %deferPtr, align 4 %5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
%stackIsNil7 = icmp eq ptr %4, null %stackIsNil7 = icmp eq %runtime._defer* %5, null
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5 br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
rundefers.loop5: ; preds = %rundefers.loophead6 rundefers.loop5: ; preds = %rundefers.loophead6
%stack.next.gep8 = getelementptr inbounds %runtime._defer, ptr %4, i32 0, i32 1 %stack.next.gep8 = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 1
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4 %stack.next9 = load %runtime._defer*, %runtime._defer** %stack.next.gep8, align 4
store ptr %stack.next9, ptr %deferPtr, align 4 store %runtime._defer* %stack.next9, %runtime._defer** %deferPtr, align 4
%callback11 = load i32, ptr %4, 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 [ switch i32 %callback11, label %rundefers.default4 [
i32 0, label %rundefers.callback012 i32 0, label %rundefers.callback012
] ]
rundefers.callback012: ; preds = %rundefers.loop5 rundefers.callback012: ; preds = %rundefers.loop5
%setjmp13 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4 %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.result14 = icmp eq i32 %setjmp13, 0 %setjmp.result15 = icmp eq i32 %setjmp14, 0
br i1 %setjmp.result14, label %5, label %lpad br i1 %setjmp.result15, label %6, label %lpad
5: ; preds = %rundefers.callback012 6: ; preds = %rundefers.callback012
call void @"main.deferSimple$1"(ptr undef) call void @"main.deferSimple$1"(i8* undef)
br label %rundefers.loophead6 br label %rundefers.loophead6
rundefers.default4: ; preds = %rundefers.loop5 rundefers.default4: ; preds = %rundefers.loop5
@@ -105,151 +109,158 @@ rundefers.end3: ; preds = %rundefers.loophead6
br label %recover br label %recover
} }
; Function Attrs: nocallback nofree nosync nounwind willreturn ; Function Attrs: nofree nosync nounwind willreturn
declare ptr @llvm.stacksave() #2 declare i8* @llvm.stacksave() #2
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #0 declare void @runtime.setupDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 { define internal void @"main.deferSimple$1"(i8* %context) unnamed_addr #1 {
entry: entry:
call void @runtime.printint32(i32 3, ptr undef) #3 call void @runtime.printint32(i32 3, i8* undef) #3
ret void ret void
} }
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #0 declare void @runtime.destroyDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*) #0
declare void @runtime.printint32(i32, ptr) #0 declare void @runtime.printint32(i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 { define hidden void @main.deferMultiple(i8* %context) unnamed_addr #1 {
entry: entry:
%defer.alloca2 = alloca { i32, ptr }, align 4 %defer.alloca2 = alloca { i32, %runtime._defer* }, align 4
%defer.alloca = alloca { i32, ptr }, align 4 %defer.alloca = alloca { i32, %runtime._defer* }, align 4
%deferPtr = alloca ptr, align 4 %deferPtr = alloca %runtime._defer*, align 4
store ptr null, ptr %deferPtr, align 4 store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4 %deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave() %0 = call i8* @llvm.stacksave()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3 call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
store i32 0, ptr %defer.alloca, align 4 %defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
%defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1 store i32 0, i32* %defer.alloca.repack, align 4
store ptr null, ptr %defer.alloca.repack22, align 4 %defer.alloca.repack26 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
store ptr %defer.alloca, ptr %deferPtr, align 4 store %runtime._defer* null, %runtime._defer** %defer.alloca.repack26, align 4
store i32 1, ptr %defer.alloca2, align 4 %1 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
%defer.alloca2.repack23 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca2, i32 0, i32 1 store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %1, align 4
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4 %defer.alloca2.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca2, i32 0, i32 0
store ptr %defer.alloca2, ptr %deferPtr, align 4 store i32 1, i32* %defer.alloca2.repack, align 4
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #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 %setjmp.result = icmp eq i32 %setjmp, 0
br i1 %setjmp.result, label %1, label %lpad br i1 %setjmp.result, label %4, label %lpad
1: ; preds = %entry 4: ; preds = %entry
call void @main.external(ptr undef) #3 call void @main.external(i8* undef) #3
br label %rundefers.loophead br label %rundefers.loophead
rundefers.loophead: ; preds = %4, %3, %1 rundefers.loophead: ; preds = %7, %6, %4
%2 = load ptr, ptr %deferPtr, align 4 %5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
%stackIsNil = icmp eq ptr %2, null %stackIsNil = icmp eq %runtime._defer* %5, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1 %stack.next.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 1
%stack.next = load ptr, ptr %stack.next.gep, align 4 %stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4 store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
%callback = load i32, ptr %2, 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 [ switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0 i32 0, label %rundefers.callback0
i32 1, label %rundefers.callback1 i32 1, label %rundefers.callback1
] ]
rundefers.callback0: ; preds = %rundefers.loop rundefers.callback0: ; preds = %rundefers.loop
%setjmp3 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4 %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.result4 = icmp eq i32 %setjmp3, 0 %setjmp.result5 = icmp eq i32 %setjmp4, 0
br i1 %setjmp.result4, label %3, label %lpad br i1 %setjmp.result5, label %6, label %lpad
3: ; preds = %rundefers.callback0 6: ; preds = %rundefers.callback0
call void @"main.deferMultiple$1"(ptr undef) call void @"main.deferMultiple$1"(i8* undef)
br label %rundefers.loophead br label %rundefers.loophead
rundefers.callback1: ; preds = %rundefers.loop rundefers.callback1: ; preds = %rundefers.loop
%setjmp5 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4 %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.result6 = icmp eq i32 %setjmp5, 0 %setjmp.result8 = icmp eq i32 %setjmp7, 0
br i1 %setjmp.result6, label %4, label %lpad br i1 %setjmp.result8, label %7, label %lpad
4: ; preds = %rundefers.callback1 7: ; preds = %rundefers.callback1
call void @"main.deferMultiple$2"(ptr undef) call void @"main.deferMultiple$2"(i8* undef)
br label %rundefers.loophead br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop rundefers.default: ; preds = %rundefers.loop
unreachable unreachable
rundefers.end: ; preds = %rundefers.loophead rundefers.end: ; preds = %rundefers.loophead
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3 call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
ret void ret void
recover: ; preds = %rundefers.end7 recover: ; preds = %rundefers.end9
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3 call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
ret void ret void
lpad: ; preds = %rundefers.callback119, %rundefers.callback016, %rundefers.callback1, %rundefers.callback0, %entry lpad: ; preds = %rundefers.callback122, %rundefers.callback018, %rundefers.callback1, %rundefers.callback0, %entry
br label %rundefers.loophead10 br label %rundefers.loophead12
rundefers.loophead10: ; preds = %7, %6, %lpad rundefers.loophead12: ; preds = %10, %9, %lpad
%5 = load ptr, ptr %deferPtr, align 4 %8 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
%stackIsNil11 = icmp eq ptr %5, null %stackIsNil13 = icmp eq %runtime._defer* %8, null
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9 br i1 %stackIsNil13, label %rundefers.end9, label %rundefers.loop11
rundefers.loop9: ; preds = %rundefers.loophead10 rundefers.loop11: ; preds = %rundefers.loophead12
%stack.next.gep12 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1 %stack.next.gep14 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 1
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4 %stack.next15 = load %runtime._defer*, %runtime._defer** %stack.next.gep14, align 4
store ptr %stack.next13, ptr %deferPtr, align 4 store %runtime._defer* %stack.next15, %runtime._defer** %deferPtr, align 4
%callback15 = load i32, ptr %5, align 4 %callback.gep16 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 0
switch i32 %callback15, label %rundefers.default8 [ %callback17 = load i32, i32* %callback.gep16, align 4
i32 0, label %rundefers.callback016 switch i32 %callback17, label %rundefers.default10 [
i32 1, label %rundefers.callback119 i32 0, label %rundefers.callback018
i32 1, label %rundefers.callback122
] ]
rundefers.callback016: ; preds = %rundefers.loop9 rundefers.callback018: ; preds = %rundefers.loop11
%setjmp17 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4 %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.result18 = icmp eq i32 %setjmp17, 0
br i1 %setjmp.result18, label %6, label %lpad
6: ; preds = %rundefers.callback016
call void @"main.deferMultiple$1"(ptr undef)
br label %rundefers.loophead10
rundefers.callback119: ; preds = %rundefers.loop9
%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}"(ptr nonnull %deferframe.buf) #4
%setjmp.result21 = icmp eq i32 %setjmp20, 0 %setjmp.result21 = icmp eq i32 %setjmp20, 0
br i1 %setjmp.result21, label %7, label %lpad br i1 %setjmp.result21, label %9, label %lpad
7: ; preds = %rundefers.callback119 9: ; preds = %rundefers.callback018
call void @"main.deferMultiple$2"(ptr undef) call void @"main.deferMultiple$1"(i8* undef)
br label %rundefers.loophead10 br label %rundefers.loophead12
rundefers.default8: ; preds = %rundefers.loop9 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 unreachable
rundefers.end7: ; preds = %rundefers.loophead10 rundefers.end9: ; preds = %rundefers.loophead12
br label %recover br label %recover
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 { define internal void @"main.deferMultiple$1"(i8* %context) unnamed_addr #1 {
entry: entry:
call void @runtime.printint32(i32 3, ptr undef) #3 call void @runtime.printint32(i32 3, i8* undef) #3
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 { define internal void @"main.deferMultiple$2"(i8* %context) unnamed_addr #1 {
entry: entry:
call void @runtime.printint32(i32 5, ptr undef) #3 call void @runtime.printint32(i32 5, i8* undef) #3
ret void 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 #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 #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 = { nocallback nofree nosync nounwind willreturn } attributes #2 = { nofree nosync nounwind willreturn }
attributes #3 = { nounwind } attributes #3 = { nounwind }
attributes #4 = { nounwind returns_twice } attributes #4 = { nounwind returns_twice }
+11 -11
View File
@@ -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 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" target triple = "wasm32-unknown-wasi"
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 { define hidden i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #1 {
entry: entry:
%positive = fcmp oge float %v, 0.000000e+00 %positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -26,25 +26,25 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 { define hidden float @main.maxu32f(i8* %context) unnamed_addr #1 {
entry: entry:
ret float 0x41F0000000000000 ret float 0x41F0000000000000
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 { define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #1 {
entry: entry:
ret i32 -1 ret i32 -1
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #1 { define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context) unnamed_addr #1 {
entry: entry:
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 } ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 { define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = uitofp i32 %v to float %0 = uitofp i32 %v to float
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000 %withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
@@ -54,7 +54,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 { define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #1 {
entry: entry:
%positive = fcmp oge float %v, 0.000000e+00 %positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -67,7 +67,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 { define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #1 {
entry: entry:
%positive = fcmp oge float %v, 0.000000e+00 %positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 2.550000e+02 %withinmax = fcmp ole float %v, 2.550000e+02
@@ -79,7 +79,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 { define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #1 {
entry: entry:
%abovemin = fcmp oge float %v, -1.280000e+02 %abovemin = fcmp oge float %v, -1.280000e+02
%belowmax = fcmp ole float %v, 1.270000e+02 %belowmax = fcmp ole float %v, 1.270000e+02
+12 -11
View File
@@ -3,42 +3,43 @@ 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 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" target triple = "wasm32-unknown-wasi"
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 { define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = icmp eq ptr %callback.funcptr, null %0 = icmp eq void ()* %callback.funcptr, null
br i1 %0, label %fpcall.throw, label %fpcall.next br i1 %0, label %fpcall.throw, label %fpcall.next
fpcall.next: ; preds = %entry fpcall.next: ; preds = %entry
call void %callback.funcptr(i32 3, ptr %callback.context) #2 %1 = bitcast void ()* %callback.funcptr to void (i32, i8*)*
call void %1(i32 3, i8* %callback.context) #2
ret void ret void
fpcall.throw: ; preds = %entry fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(ptr undef) #2 call void @runtime.nilPanic(i8* undef) #2
unreachable unreachable
} }
declare void @runtime.nilPanic(ptr) #0 declare void @runtime.nilPanic(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.bar(ptr %context) unnamed_addr #1 { define hidden void @main.bar(i8* %context) unnamed_addr #1 {
entry: entry:
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef) call void @main.foo(i8* undef, void ()* bitcast (void (i32, i8*)* @main.someFunc to void ()*), i8* undef)
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 { define hidden void @main.someFunc(i32 %arg0, i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
+92 -94
View File
@@ -3,135 +3,133 @@ source_filename = "gc.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { ptr, i32, ptr, ptr, i32 } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
%runtime._interface = type { i32, ptr } %runtime.interfaceMethodInfo = type { i8*, i32 }
%runtime._interface = type { i32, i8* }
@main.scalar1 = hidden global ptr null, align 4 @main.scalar1 = hidden global i8* null, align 4
@main.scalar2 = hidden global ptr null, align 4 @main.scalar2 = hidden global i32* null, align 4
@main.scalar3 = hidden global ptr null, align 4 @main.scalar3 = hidden global i64* null, align 4
@main.scalar4 = hidden global ptr null, align 4 @main.scalar4 = hidden global float* null, align 4
@main.array1 = hidden global ptr null, align 4 @main.array1 = hidden global [3 x i8]* null, align 4
@main.array2 = hidden global ptr null, align 4 @main.array2 = hidden global [71 x i8]* null, align 4
@main.array3 = hidden global ptr null, align 4 @main.array3 = hidden global [3 x i8*]* null, align 4
@main.struct1 = hidden global ptr null, align 4 @main.struct1 = hidden global {}* null, align 4
@main.struct2 = hidden global ptr null, align 4 @main.struct2 = hidden global { i32, i32 }* null, align 4
@main.struct3 = hidden global ptr null, align 4 @main.struct3 = hidden global { i8*, [60 x i32], i8* }* null, align 4
@main.struct4 = hidden global ptr null, align 4 @main.struct4 = hidden global { i8*, [61 x i32] }* null, align 4
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 8 @main.slice1 = hidden global { i8*, i32, i32 } zeroinitializer, align 8
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 8 @main.slice2 = hidden global { i32**, i32, i32 } zeroinitializer, align 8
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8 @main.slice3 = hidden global { { i8*, i32, i32 }*, i32, i32 } zeroinitializer, align 8
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " } @"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c" \00\00\00\00\00\00\01" }
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" } @"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\00\00\00\00\00\00\00\01" }
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { ptr null, i32 0, ptr null, ptr @"reflect/types.type:pointer:basic:complex128", i32 0 } @"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 { ptr @"reflect/types.type:basic:complex128", i32 0, ptr null, ptr null, 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 ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 { define hidden void @main.newScalar(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new = call ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new, i8** @main.scalar1, align 4
store ptr %new, ptr @main.scalar1, align 4 %new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new1 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4
store ptr %new1, ptr @main.scalar2, align 4 %new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new2 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4
store ptr %new2, ptr @main.scalar3, align 4 %new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new3 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
store ptr %new3, ptr @main.scalar4, align 4
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.newArray(ptr %context) unnamed_addr #1 { define hidden void @main.newArray(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new = call ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4
store ptr %new, ptr @main.array1, align 4 %new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new1 = call ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4
store ptr %new1, ptr @main.array2, align 4 %new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
%new2 = call ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
store ptr %new2, ptr @main.array3, align 4
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 { define hidden void @main.newStruct(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new = call ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4
store ptr %new, ptr @main.struct1, align 4 %new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%new1 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4
store ptr %new1, ptr @main.struct2, align 4 %new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef) #2
%new2 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
store ptr %new2, ptr @main.struct3, align 4 %new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #2
%new3 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2 store i8* %new3, i8** bitcast ({ i8*, [61 x i32] }** @main.struct4 to i8**), align 4
store ptr %new3, ptr @main.struct4, align 4
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 { define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #2
%new = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #2 %0 = bitcast i8* %new to { i8*, void ()* }*
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
ret ptr %new ret { i8*, void ()* }* %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 { define hidden void @main.makeSlice(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%makeslice = call ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2 store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8
store ptr %makeslice, ptr @main.slice1, 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, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @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
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 8 %makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
%makeslice1 = call ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #2 store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8
store ptr %makeslice1, ptr @main.slice2, 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, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @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
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 8 %makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef) #2
%makeslice3 = call ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #2 store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8
store ptr %makeslice3, ptr @main.slice3, 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, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @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
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 8
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #2
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 %.repack = bitcast i8* %0 to double*
store double %v.r, ptr %0, align 8 store double %v.r, double* %.repack, align 8
%.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1 %.repack1 = getelementptr inbounds i8, i8* %0, i32 8
store double %v.i, ptr %.repack1, align 8 %1 = bitcast i8* %.repack1 to double*
%1 = insertvalue %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:basic:complex128" to i32), ptr undef }, ptr %0, 1 store double %v.i, double* %1, align 8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 %2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1
ret %runtime._interface %1 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
ret %runtime._interface %2
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
-15
View File
@@ -1,15 +0,0 @@
package main
import "unsafe"
func unsafeSliceData(s []int) *int {
return unsafe.SliceData(s)
}
func unsafeString(ptr *byte, len int16) string {
return unsafe.String(ptr, len)
}
func unsafeStringData(s string) *byte {
return unsafe.StringData(s)
}
-61
View File
@@ -1,61 +0,0 @@
; ModuleID = 'go1.20.go'
source_filename = "go1.20.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"
%runtime._string = type { ptr, i32 }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %s.data
}
; Function Attrs: nounwind
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = icmp slt i16 %len, 0
%1 = icmp eq ptr %ptr, null
%2 = icmp ne i16 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.String.throw, label %unsafe.String.next
unsafe.String.next: ; preds = %entry
%5 = zext i16 %len to i32
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
%7 = insertvalue %runtime._string %6, i32 %5, 1
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
ret %runtime._string %7
unsafe.String.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #2
unreachable
}
declare void @runtime.unsafeSlicePanic(ptr) #0
; Function Attrs: nounwind
define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %s.data
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
+107 -82
View File
@@ -3,176 +3,201 @@ source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" 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" target triple = "thumbv7m-unknown-unknown-eabi"
%runtime._string = type { ptr, i32 } %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", 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 @"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #8 %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 (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr 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 ret void
} }
declare void @main.regularFunction(i32, ptr) #0 declare void @main.regularFunction(i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 { define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #8 call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
ret void ret void
} }
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #0 declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*) #0
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0 declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #8 %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 (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr 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 ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 { define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 { define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef) call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
%n = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8 %n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
store i32 3, ptr %n, align 4 %0 = bitcast i8* %n to i32*
%0 = call ptr @runtime.alloc(i32 8, ptr null, ptr undef) #8 store i32 3, i32* %0, align 4
store i32 5, ptr %0, align 4 %1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1 %2 = bitcast i8* %1 to i32*
store ptr %n, ptr %1, align 4 store i32 5, i32* %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #8 %3 = getelementptr inbounds i8, i8* %1, i32 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8 %4 = bitcast i8* %3 to i8**
%2 = load i32, ptr %n, align 4 store i8* %n, i8** %4, align 4
call void @runtime.printint32(i32 %2, ptr undef) #8 %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) #8
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 { define internal void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
entry: entry:
store i32 7, ptr %context, align 4 %unpack.ptr = bitcast i8* %context to i32*
store i32 7, i32* %unpack.ptr, align 4
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 { define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = bitcast i8* %0 to i32*
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1 %2 = load i32, i32* %1, align 4
%3 = load ptr, ptr %2, align 4 %3 = getelementptr inbounds i8, i8* %0, i32 4
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3) %4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
ret void ret void
} }
declare void @runtime.printint32(i32, ptr) #0 declare void @runtime.printint32(i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call ptr @runtime.alloc(i32 12, ptr null, ptr undef) #8 %0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
store i32 5, ptr %0, align 4 %1 = bitcast i8* %0 to i32*
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1 store i32 5, i32* %1, align 4
store ptr %fn.context, ptr %1, align 4 %2 = getelementptr inbounds i8, i8* %0, i32 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2 %3 = bitcast i8* %2 to i8**
store ptr %fn.funcptr, ptr %2, align 4 store i8* %fn.context, i8** %3, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #8 %4 = getelementptr inbounds i8, i8* %0, i32 8
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #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) #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 ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #5 { define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = bitcast i8* %0 to i32*
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1 %2 = load i32, i32* %1, align 4
%3 = load ptr, ptr %2, align 4 %3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2 %4 = bitcast i8* %3 to i8**
%5 = load ptr, ptr %4, align 4 %5 = load i8*, i8** %4, align 4
call void %5(i32 %1, ptr %3) #8 %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) #8
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 { define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
entry: entry:
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #8 %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 ret void
} }
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #0 declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
entry: entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #8 call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
ret void ret void
} }
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0 declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8 %0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
store ptr %itf.value, ptr %0, align 4 %1 = bitcast i8* %0 to i8**
%1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1 store i8* %itf.value, i8** %1, align 4
store ptr @"main$string", ptr %1, align 4 %2 = getelementptr inbounds i8, i8* %0, i32 4
%.repack1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1, i32 1 %.repack = bitcast i8* %2 to i8**
store i32 4, ptr %.repack1, align 4 store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 2 %.repack1 = getelementptr inbounds i8, i8* %0, i32 8
store i32 %itf.typecode, ptr %2, align 4 %3 = bitcast i8* %.repack1 to i32*
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #8 store i32 4, i32* %3, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8 %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) #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 ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, i32, ptr) #6 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
entry: entry:
%1 = load ptr, ptr %0, align 4 %1 = bitcast i8* %0 to i8**
%2 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 1 %2 = load i8*, i8** %1, align 4
%3 = load ptr, ptr %2, align 4 %3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 2 %4 = bitcast i8* %3 to i8**
%5 = load i32, ptr %4, align 4 %5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 3 %6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = load i32, ptr %6, align 4 %7 = bitcast i8* %6 to i32*
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, i32 %7, ptr undef) #8 %8 = load i32, i32* %7, align 4
%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) #8
ret void ret void
} }
+115 -92
View File
@@ -3,187 +3,210 @@ source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 } %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", 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* }
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1 @"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr undef) #8 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 ret void
} }
declare void @main.regularFunction(i32, ptr) #0 declare void @main.regularFunction(i32, i8*) #0
declare void @runtime.deadlock(ptr) #0 declare void @runtime.deadlock(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 { define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #8 call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
call void @runtime.deadlock(ptr undef) #8 call void @runtime.deadlock(i8* undef) #8
unreachable unreachable
} }
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0 declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr 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 16384, i8* undef) #8
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 { define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 { define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef) call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
call void @runtime.deadlock(ptr undef) #8 call void @runtime.deadlock(i8* undef) #8
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
%n = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8 %0 = bitcast i8* %n to i32*
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8 call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
store i32 3, ptr %n, align 4 store i32 3, i32* %0, align 4
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8 call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #8 call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #8
%0 = call ptr @runtime.alloc(i32 8, ptr null, ptr undef) #8 %1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8 call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #8
store i32 5, ptr %0, align 4 %2 = bitcast i8* %1 to i32*
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1 store i32 5, i32* %2, align 4
store ptr %n, ptr %1, align 4 %3 = getelementptr inbounds i8, i8* %1, i32 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8 %4 = bitcast i8* %3 to i8**
%2 = load i32, ptr %n, align 4 store i8* %n, i8** %4, align 4
call void @runtime.printint32(i32 %2, ptr undef) #8 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) #8
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 { define internal void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
entry: entry:
store i32 7, ptr %context, align 4 %unpack.ptr = bitcast i8* %context to i32*
store i32 7, i32* %unpack.ptr, align 4
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 { define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = bitcast i8* %0 to i32*
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1 %2 = load i32, i32* %1, align 4
%3 = load ptr, ptr %2, align 4 %3 = getelementptr inbounds i8, i8* %0, i32 4
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3) %4 = bitcast i8* %3 to i8**
call void @runtime.deadlock(ptr undef) #8 %5 = load i8*, i8** %4, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
call void @runtime.deadlock(i8* undef) #8
unreachable unreachable
} }
declare void @runtime.printint32(i32, ptr) #0 declare void @runtime.printint32(i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
%0 = call ptr @runtime.alloc(i32 12, ptr null, ptr undef) #8 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8 %1 = bitcast i8* %0 to i32*
store i32 5, ptr %0, align 4 store i32 5, i32* %1, align 4
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1 %2 = getelementptr inbounds i8, i8* %0, i32 4
store ptr %fn.context, ptr %1, align 4 %3 = bitcast i8* %2 to i8**
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2 store i8* %fn.context, i8** %3, align 4
store ptr %fn.funcptr, ptr %2, align 4 %4 = getelementptr inbounds i8, i8* %0, i32 8
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 16384, ptr undef) #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) #8
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #5 { define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = bitcast i8* %0 to i32*
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1 %2 = load i32, i32* %1, align 4
%3 = load ptr, ptr %2, align 4 %3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2 %4 = bitcast i8* %3 to i8**
%5 = load ptr, ptr %4, align 4 %5 = load i8*, i8** %4, align 4
call void %5(i32 %1, ptr %3) #8 %6 = getelementptr inbounds i8, i8* %0, i32 8
call void @runtime.deadlock(ptr undef) #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) #8
call void @runtime.deadlock(i8* undef) #8
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 { define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
entry: entry:
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #8 %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 ret void
} }
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #0 declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
entry: entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #8 call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
ret void ret void
} }
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0 declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8 %1 = bitcast i8* %0 to i8**
store ptr %itf.value, ptr %0, align 4 store i8* %itf.value, i8** %1, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1 %2 = getelementptr inbounds i8, i8* %0, i32 4
store ptr @"main$string", ptr %1, align 4 %.repack = bitcast i8* %2 to i8**
%.repack1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1, i32 1 store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
store i32 4, ptr %.repack1, align 4 %.repack1 = getelementptr inbounds i8, i8* %0, i32 8
%2 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 2 %3 = bitcast i8* %.repack1 to i32*
store i32 %itf.typecode, ptr %2, align 4 store i32 4, i32* %3, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8 %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) #8
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, i32, ptr) #6 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
entry: entry:
%1 = load ptr, ptr %0, align 4 %1 = bitcast i8* %0 to i8**
%2 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 1 %2 = load i8*, i8** %1, align 4
%3 = load ptr, ptr %2, align 4 %3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 2 %4 = bitcast i8* %3 to i8**
%5 = load i32, ptr %4, align 4 %5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 3 %6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = load i32, ptr %6, align 4 %7 = bitcast i8* %6 to i32*
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, i32 %7, ptr undef) #8 %8 = load i32, i32* %7, align 4
call void @runtime.deadlock(ptr undef) #8 %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) #8
call void @runtime.deadlock(i8* undef) #8
unreachable unreachable
} }
+41 -45
View File
@@ -3,74 +3,71 @@ source_filename = "interface.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { ptr, i32, ptr, ptr, i32 } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
%runtime._interface = type { i32, ptr } %runtime.interfaceMethodInfo = type { i8*, i32 }
%runtime._string = type { ptr, i32 } %runtime._interface = type { i32, i8* }
%runtime._string = type { i8*, i32 }
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { ptr null, i32 0, ptr null, ptr @"reflect/types.type:pointer:basic:int", i32 0 } @"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int", i32 0 }
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:basic:int", i32 0, ptr null, ptr null, i32 0 } @"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:named:error", i32 0, ptr null, ptr null, i32 0 } @"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, ptr null, ptr @"reflect/types.type:pointer:named:error", i32 ptrtoint (ptr @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) } @"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error", i32 ptrtoint (i1 (i32)* @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.interface:interface{Error() string}$interface", i32 0, ptr null, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 ptrtoint (ptr @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) } @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 ptrtoint (i1 (i32)* @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1 @"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x ptr] [ptr @"reflect/methods.Error() string"] @"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, ptr null, ptr null, i32 0 } @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, ptr null, ptr null, i32 0 } @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.interface:interface{String() string}$interface", i32 0, ptr null, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", i32 ptrtoint (ptr @"interface:{String:func:{}{basic:string}}.$typeassert" to i32) } @"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", i32 ptrtoint (i1 (i32)* @"interface:{String:func:{}{basic:string}}.$typeassert" to i32) }
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1 @"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x ptr] [ptr @"reflect/methods.String() string"] @"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"]
@"reflect/types.typeid:basic:int" = external constant i8 @"reflect/types.typeid:basic:int" = external constant i8
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 call void @runtime.trackPointer(i8* null, i8* undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:basic:int" to i32), ptr null }
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 call void @runtime.trackPointer(i8* null, i8* undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:basic:int" to i32), ptr null }
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 call void @runtime.trackPointer(i8* null, i8* undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:named:error" to i32), ptr null }
} }
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2 declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 call void @runtime.trackPointer(i8* null, i8* undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), ptr null }
} }
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #3 declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isInt(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
entry: entry:
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6 %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 br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry typeassert.next: ; preds = %typeassert.ok, %entry
@@ -80,10 +77,10 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next br label %typeassert.next
} }
declare i1 @runtime.typeAssert(i32, ptr dereferenceable_or_null(1), ptr) #0 declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isError(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6 %0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next br i1 %0, label %typeassert.ok, label %typeassert.next
@@ -96,7 +93,7 @@ typeassert.ok: ; preds = %entry
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isStringer(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6 %0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next br i1 %0, label %typeassert.ok, label %typeassert.next
@@ -109,25 +106,24 @@ typeassert.ok: ; preds = %entry
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, i32 %itf.typecode, ptr undef) #6 %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 ret i8 %0
} }
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, i32, ptr) #4 declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #4
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #6
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, i32 %itf.typecode, ptr undef) #6
%1 = extractvalue %runtime._string %0, 0 %1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(i8* %1, i8* undef) #6
ret %runtime._string %0 ret %runtime._string %0
} }
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, i32, ptr) #5 declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #5
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
+36 -38
View File
@@ -3,78 +3,76 @@ 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 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" target triple = "wasm32-unknown-wasi"
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #1 { define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #1 {
entry: entry:
ret [0 x i32] zeroinitializer ret [0 x i32] zeroinitializer
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 { define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = bitcast i8* %x to i32*
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* %x, i8* undef) #2
ret ptr %x ret i32* %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 { define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = bitcast i32* %x to i8*
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* %0, i8* undef) #2
ret ptr %x ret i8* %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 { define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 call void @runtime.trackPointer(i8* %x, i8* undef) #2
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 ret i8* %x
ret ptr %x
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.pointerUnsafeGEPFixedOffset(ptr dereferenceable_or_null(1) %ptr, ptr %context) unnamed_addr #1 { define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %0 = getelementptr inbounds i8, i8* %ptr, i32 10
%0 = getelementptr inbounds i8, ptr %ptr, i32 10 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 ret i8* %0
ret ptr %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.pointerUnsafeGEPByteOffset(ptr dereferenceable_or_null(1) %ptr, i32 %offset, ptr %context) unnamed_addr #1 { define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
%0 = getelementptr inbounds i8, ptr %ptr, i32 %offset call void @runtime.trackPointer(i8* %0, i8* undef) #2
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* %0, i8* undef) #2
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2 ret i8* %0
ret ptr %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.pointerUnsafeGEPIntOffset(ptr dereferenceable_or_null(4) %ptr, i32 %offset, ptr %context) unnamed_addr #1 { define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* %0, i8* undef) #2
%0 = shl i32 %offset, 2 %1 = getelementptr i32, i32* %ptr, i32 %offset
%1 = getelementptr inbounds i8, ptr %ptr, i32 %0 %2 = bitcast i32* %1 to i8*
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* %2, i8* undef) #2
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2 %3 = bitcast i32* %1 to i8*
ret ptr %1 call void @runtime.trackPointer(i8* %3, i8* undef) #2
ret i32* %1
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
+9 -10
View File
@@ -6,17 +6,16 @@ target triple = "wasm32-unknown-wasi"
@extern_global = external global [0 x i8], align 1 @extern_global = external global [0 x i8], align 1
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32 @main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16 @main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
@llvm.used = appending global [2 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection]
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4 @main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
@undefinedGlobalNotInSection = external global i32, align 4 @undefinedGlobalNotInSection = external global i32, align 4
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024 @main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
@@ -28,27 +27,27 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 { define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
declare void @somepkg.someFunction2(ptr) #0 declare void @somepkg.someFunction2(i8*) #0
; Function Attrs: inlinehint nounwind ; Function Attrs: inlinehint nounwind
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 { define hidden void @main.inlineFunc(i8* %context) unnamed_addr #3 {
entry: entry:
ret void ret void
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 { define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #4 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.functionInSection(ptr %context) unnamed_addr #1 section ".special_function_section" { define hidden void @main.functionInSection(i8* %context) unnamed_addr #1 section ".special_function_section" {
entry: entry:
ret void ret void
} }
@@ -59,7 +58,7 @@ entry:
ret void ret void
} }
declare void @main.undefinedFunctionNotInSection(ptr) #0 declare void @main.undefinedFunctionNotInSection(i8*) #0
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
+141 -136
View File
@@ -3,282 +3,286 @@ 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 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" target triple = "wasm32-unknown-wasi"
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 { define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
entry: entry:
ret i32 %ints.len ret i32 %ints.len
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 { define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
entry: entry:
ret i32 %ints.cap ret i32 %ints.cap
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #1 { define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #1 {
entry: entry:
%.not = icmp ult i32 %index, %ints.len %.not = icmp ult i32 %index, %ints.len
br i1 %.not, label %lookup.next, label %lookup.throw br i1 %.not, label %lookup.next, label %lookup.throw
lookup.next: ; preds = %entry lookup.next: ; preds = %entry
%0 = getelementptr inbounds i32, ptr %ints.data, i32 %index %0 = getelementptr inbounds i32, i32* %ints.data, i32 %index
%1 = load i32, ptr %0, align 4 %1 = load i32, i32* %0, align 4
ret i32 %1 ret i32 %1
lookup.throw: ; preds = %entry lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(ptr undef) #2 call void @runtime.lookupPanic(i8* undef) #2
unreachable unreachable
} }
declare void @runtime.lookupPanic(ptr) #0 declare void @runtime.lookupPanic(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 { define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%varargs = call ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #2 %0 = bitcast i8* %varargs to i32*
store i32 1, ptr %varargs, align 4 store i32 1, i32* %0, align 4
%0 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 1 %1 = getelementptr inbounds i8, i8* %varargs, i32 4
store i32 2, ptr %0, align 4 %2 = bitcast i8* %1 to i32*
%1 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 2 store i32 2, i32* %2, align 4
store i32 3, ptr %1, align 4 %3 = getelementptr inbounds i8, i8* %varargs, i32 8
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr undef) #2 %4 = bitcast i8* %3 to i32*
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0 store i32 3, i32* %4, align 4
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1 %append.srcPtr = bitcast i32* %ints.data to i8*
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2 %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
%2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0 %append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
%3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1 %append.newBuf = bitcast i8* %append.newPtr to i32*
%4 = insertvalue { ptr, i32, i32 } %3, i32 %append.newCap, 2 %append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #2 %append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
ret { ptr, i32, i32 } %4 %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) #2
ret { i32*, i32, i32 } %7
} }
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr) #0 declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #1 { 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: entry:
%stackalloc = alloca i8, align 1 %append.srcPtr = bitcast i32* %ints.data to i8*
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr undef) #2 %append.srcPtr1 = bitcast i32* %added.data to i8*
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 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.newLen = extractvalue { ptr, i32, i32 } %append.new, 1 %append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2 %append.newBuf = bitcast i8* %append.newPtr to i32*
%0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0 %append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
%1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1 %append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
%2 = insertvalue { ptr, i32, i32 } %1, i32 %append.newCap, 2 %0 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #2 %1 = insertvalue { i32*, i32, i32 } %0, i32 %append.newLen, 1
ret { ptr, i32, i32 } %2 %2 = insertvalue { i32*, i32, i32 } %1, i32 %append.newCap, 2
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #2
ret { i32*, i32, i32 } %2
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 { 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: entry:
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 4, ptr undef) #2 %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) #2
ret i32 %copy.n ret i32 %copy.n
} }
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #0 declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #1 { define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp slt i32 %len, 0 %slice.maxcap = icmp slt i32 %len, 0
br i1 %slice.maxcap, label %slice.throw, label %slice.next br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.buf = call ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
ret { ptr, i32, i32 } %2 ret { i8*, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #2 call void @runtime.slicePanic(i8* undef) #2
unreachable unreachable
} }
declare void @runtime.slicePanic(ptr) #0 declare void @runtime.slicePanic(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 { define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp slt i32 %len, 0 %slice.maxcap = icmp slt i32 %len, 0
br i1 %slice.maxcap, label %slice.throw, label %slice.next br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.cap = shl i32 %len, 1 %makeslice.cap = shl i32 %len, 1
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %makeslice.array = bitcast i8* %makeslice.buf to i16*
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 %2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
ret { ptr, i32, i32 } %2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
ret { i16*, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #2 call void @runtime.slicePanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #1 { define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp ugt i32 %len, 1431655765 %slice.maxcap = icmp ugt i32 %len, 1431655765
br i1 %slice.maxcap, label %slice.throw, label %slice.next br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.cap = mul i32 %len, 3 %makeslice.cap = mul i32 %len, 3
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 %2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
ret { ptr, i32, i32 } %2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
ret { [3 x i8]*, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #2 call void @runtime.slicePanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #1 { define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp ugt i32 %len, 1073741823 %slice.maxcap = icmp ugt i32 %len, 1073741823
br i1 %slice.maxcap, label %slice.throw, label %slice.next br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.cap = shl i32 %len, 2 %makeslice.cap = shl i32 %len, 2
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %makeslice.array = bitcast i8* %makeslice.buf to i32*
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 %2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
ret { ptr, i32, i32 } %2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
ret { i32*, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #2 call void @runtime.slicePanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #1 { define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = getelementptr i8, i8* %p, i32 %len
%0 = getelementptr i8, ptr %p, i32 %len call void @runtime.trackPointer(i8* %0, i8* undef) #2
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2 ret i8* %0
ret ptr %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #1 { define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%0 = trunc i64 %len to i32 %0 = trunc i64 %len to i32
%1 = getelementptr i8, ptr %p, i32 %0 %1 = getelementptr i8, i8* %p, i32 %0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* %1, i8* undef) #2
ret ptr %1 ret i8* %1
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 { define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = icmp ult i32 %s.len, 4 %0 = icmp ult i32 %s.len, 4
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.next: ; preds = %entry slicetoarray.next: ; preds = %entry
ret ptr %s.data %1 = bitcast i32* %s.data to [4 x i32]*
ret [4 x i32]* %1
slicetoarray.throw: ; preds = %entry slicetoarray.throw: ; preds = %entry
call void @runtime.sliceToArrayPointerPanic(ptr undef) #2 call void @runtime.sliceToArrayPointerPanic(i8* undef) #2
unreachable unreachable
} }
declare void @runtime.sliceToArrayPointerPanic(ptr) #0 declare void @runtime.sliceToArrayPointerPanic(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 { define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
%makeslice = call ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2
br i1 false, label %slicetoarray.throw, label %slicetoarray.next br i1 false, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.next: ; preds = %entry slicetoarray.next: ; preds = %entry
ret ptr %makeslice %0 = bitcast i8* %makeslice to [4 x i32]*
ret [4 x i32]* %0
slicetoarray.throw: ; preds = %entry slicetoarray.throw: ; preds = %entry
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #1 { define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%0 = icmp ugt i32 %len, 1073741823 %0 = icmp ugt i32 %len, 1073741823
%1 = icmp eq ptr %ptr, null %1 = icmp eq i32* %ptr, null
%2 = icmp ne i32 %len, 0 %2 = icmp ne i32 %len, 0
%3 = and i1 %1, %2 %3 = and i1 %1, %2
%4 = or i1 %3, %0 %4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.next: ; preds = %entry unsafe.Slice.next: ; preds = %entry
%5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1 %6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
%7 = insertvalue { ptr, i32, i32 } %6, i32 %len, 2 %7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %8 = bitcast i32* %ptr to i8*
ret { ptr, i32, i32 } %7 call void @runtime.trackPointer(i8* %8, i8* undef) #2
ret { i32*, i32, i32 } %7
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #2 call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable unreachable
} }
declare void @runtime.unsafeSlicePanic(ptr) #0 declare void @runtime.unsafeSlicePanic(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 { define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %0 = icmp eq i8* %ptr, null
%0 = icmp eq ptr %ptr, null
%1 = icmp ne i16 %len, 0 %1 = icmp ne i16 %len, 0
%2 = and i1 %0, %1 %2 = and i1 %0, %1
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.next: ; preds = %entry unsafe.Slice.next: ; preds = %entry
%3 = zext i16 %len to i32 %3 = zext i16 %len to i32
%4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
%5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1 %5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
%6 = insertvalue { ptr, i32, i32 } %5, i32 %3, 2 %6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
ret { ptr, i32, i32 } %6 ret { i8*, i32, i32 } %6
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #2 call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 { define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%0 = icmp ugt i64 %len, 1073741823 %0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq ptr %ptr, null %1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0 %2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2 %3 = and i1 %1, %2
%4 = or i1 %3, %0 %4 = or i1 %3, %0
@@ -286,23 +290,23 @@ entry:
unsafe.Slice.next: ; preds = %entry unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32 %5 = trunc i64 %len to i32
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1 %7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2 %8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %9 = bitcast i32* %ptr to i8*
ret { ptr, i32, i32 } %8 call void @runtime.trackPointer(i8* %9, i8* undef) #2
ret { i32*, i32, i32 } %8
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #2 call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 { define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1
%0 = icmp ugt i64 %len, 1073741823 %0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq ptr %ptr, null %1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0 %2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2 %3 = and i1 %1, %2
%4 = or i1 %3, %0 %4 = or i1 %3, %0
@@ -310,14 +314,15 @@ entry:
unsafe.Slice.next: ; preds = %entry unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32 %5 = trunc i64 %len to i32
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1 %7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2 %8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %9 = bitcast i32* %ptr to i8*
ret { ptr, i32, i32 } %8 call void @runtime.trackPointer(i8* %9, i8* undef) #2
ret { i32*, i32, i32 } %8
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #2 call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable unreachable
} }
+25 -25
View File
@@ -3,96 +3,96 @@ source_filename = "string.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 } %runtime._string = type { i8*, i32 }
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1 @"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #1 { define hidden %runtime._string @main.someString(i8* %context) unnamed_addr #1 {
entry: entry:
ret %runtime._string { ptr @"main$string", i32 3 } ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main$string", i32 0, i32 0), i32 3 }
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #1 { define hidden %runtime._string @main.zeroLengthString(i8* %context) unnamed_addr #1 {
entry: entry:
ret %runtime._string zeroinitializer ret %runtime._string zeroinitializer
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #1 { define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context) unnamed_addr #1 {
entry: entry:
ret i32 %s.len ret i32 %s.len
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #1 { define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context) unnamed_addr #1 {
entry: entry:
%.not = icmp ult i32 %index, %s.len %.not = icmp ult i32 %index, %s.len
br i1 %.not, label %lookup.next, label %lookup.throw br i1 %.not, label %lookup.next, label %lookup.throw
lookup.next: ; preds = %entry lookup.next: ; preds = %entry
%0 = getelementptr inbounds i8, ptr %s.data, i32 %index %0 = getelementptr inbounds i8, i8* %s.data, i32 %index
%1 = load i8, ptr %0, align 1 %1 = load i8, i8* %0, align 1
ret i8 %1 ret i8 %1
lookup.throw: ; preds = %entry lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(ptr undef) #2 call void @runtime.lookupPanic(i8* undef) #2
unreachable unreachable
} }
declare void @runtime.lookupPanic(ptr) #0 declare void @runtime.lookupPanic(i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 { define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2 %0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #2
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #0 declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 { define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2 %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 %1 = xor i1 %0, true
ret i1 %1 ret i1 %1
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 { define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #2 %0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef) #2
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #0 declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #1 { define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context) unnamed_addr #1 {
entry: entry:
%0 = zext i8 %x to i32 %0 = zext i8 %x to i32
%.not = icmp ult i32 %0, %s.len %.not = icmp ult i32 %0, %s.len
br i1 %.not, label %lookup.next, label %lookup.throw br i1 %.not, label %lookup.next, label %lookup.throw
lookup.next: ; preds = %entry lookup.next: ; preds = %entry
%1 = getelementptr inbounds i8, ptr %s.data, i32 %0 %1 = getelementptr inbounds i8, i8* %s.data, i32 %0
%2 = load i8, ptr %1, align 1 %2 = load i8, i8* %1, align 1
ret i8 %2 ret i8 %2
lookup.throw: ; preds = %entry lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(ptr undef) #2 call void @runtime.lookupPanic(i8* undef) #2
unreachable unreachable
} }
+1 -4
View File
@@ -1,7 +1,5 @@
package compiler package compiler
import "go/types"
// This file implements volatile loads/stores in runtime/volatile.LoadT and // This file implements volatile loads/stores in runtime/volatile.LoadT and
// runtime/volatile.StoreT as compiler builtins. // runtime/volatile.StoreT as compiler builtins.
@@ -11,8 +9,7 @@ func (b *builder) createVolatileLoad() {
b.createFunctionStart(true) b.createFunctionStart(true)
addr := b.getValue(b.fn.Params[0]) addr := b.getValue(b.fn.Params[0])
b.createNilCheck(b.fn.Params[0], addr, "deref") b.createNilCheck(b.fn.Params[0], addr, "deref")
valType := b.getLLVMType(b.fn.Params[0].Type().(*types.Pointer).Elem()) val := b.CreateLoad(addr, "")
val := b.CreateLoad(valType, addr, "")
val.SetVolatile(true) val.SetVolatile(true)
b.CreateRet(val) b.CreateRet(val)
} }
+4 -3
View File
@@ -14,10 +14,10 @@ require (
github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-tty v0.0.4 github.com/mattn/go-tty v0.0.4
go.bug.st/serial v1.3.5 go.bug.st/serial v1.3.5
golang.org/x/sys v0.4.0 golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 golang.org/x/tools v0.1.11
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32 tinygo.org/x/go-llvm v0.0.0-20220802112859-5bb0b77907a7
) )
require ( require (
@@ -29,4 +29,5 @@ require (
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect github.com/mattn/go-isatty v0.0.12 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
) )
+8 -7
View File
@@ -46,7 +46,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
go.bug.st/serial v1.3.5 h1:k50SqGZCnHZ2MiBQgzccXWG+kd/XpOs1jUljpDDKzaE= go.bug.st/serial v1.3.5 h1:k50SqGZCnHZ2MiBQgzccXWG+kd/XpOs1jUljpDDKzaE=
go.bug.st/serial v1.3.5/go.mod h1:z8CesKorE90Qr/oRSJiEuvzYRKol9r/anJZEb5kt304= go.bug.st/serial v1.3.5/go.mod h1:z8CesKorE90Qr/oRSJiEuvzYRKol9r/anJZEb5kt304=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= 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/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -54,14 +55,14 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 h1:LHoToPgySGSr2NcUHbjENAidHz38RkKaNmmntwn9TjI= golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32 h1:LvdmoXncO43m2cws1chRB2hkLBAxfN6CbSjDI7+gk4Y= tinygo.org/x/go-llvm v0.0.0-20220802112859-5bb0b77907a7 h1:nSLR52mUw7DPQQVA3ZJFH63zjU4ME84fKiin6mdnYWc=
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0= tinygo.org/x/go-llvm v0.0.0-20220802112859-5bb0b77907a7/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo. // Version of TinyGo.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const Version = "0.27.0" const Version = "0.26.0-dev"
var ( var (
// This variable is set at build time using -ldflags parameters. // This variable is set at build time using -ldflags parameters.
+7 -14
View File
@@ -209,7 +209,7 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
case llvm.Alloca: case llvm.Alloca:
// Alloca allocates stack space for local variables. // Alloca allocates stack space for local variables.
numElements := r.getValue(inst.llvmInst.Operand(0)).(literalValue).value.(uint32) numElements := r.getValue(inst.llvmInst.Operand(0)).(literalValue).value.(uint32)
elementSize := r.targetData.TypeAllocSize(inst.llvmInst.AllocatedType()) elementSize := r.targetData.TypeAllocSize(inst.llvmInst.Type().ElementType())
inst.operands = []value{ inst.operands = []value{
literalValue{elementSize * uint64(numElements)}, literalValue{elementSize * uint64(numElements)},
} }
@@ -218,17 +218,17 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
inst.name = llvmInst.Name() inst.name = llvmInst.Name()
ptr := llvmInst.Operand(0) ptr := llvmInst.Operand(0)
n := llvmInst.OperandsCount() n := llvmInst.OperandsCount()
elementType := llvmInst.GEPSourceElementType() elementType := ptr.Type().ElementType()
// gep: [source ptr, dest value size, pairs of indices...] // gep: [source ptr, dest value size, pairs of indices...]
inst.operands = []value{ inst.operands = []value{
r.getValue(ptr), r.getValue(ptr),
literalValue{r.targetData.TypeAllocSize(llvmInst.Type().ElementType())},
r.getValue(llvmInst.Operand(1)), r.getValue(llvmInst.Operand(1)),
literalValue{r.targetData.TypeAllocSize(elementType)}, literalValue{r.targetData.TypeAllocSize(elementType)},
} }
for i := 2; i < n; i++ { for i := 2; i < n; i++ {
operand := r.getValue(llvmInst.Operand(i)) operand := r.getValue(llvmInst.Operand(i))
switch elementType.TypeKind() { if elementType.TypeKind() == llvm.StructTypeKind {
case llvm.StructTypeKind:
index := operand.(literalValue).value.(uint32) index := operand.(literalValue).value.(uint32)
elementOffset := r.targetData.ElementOffset(elementType, int(index)) elementOffset := r.targetData.ElementOffset(elementType, int(index))
// Encode operands in a special way. The elementOffset // Encode operands in a special way. The elementOffset
@@ -242,15 +242,12 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
// runtime. // runtime.
inst.operands = append(inst.operands, literalValue{elementOffset}, literalValue{^uint64(index)}) inst.operands = append(inst.operands, literalValue{elementOffset}, literalValue{^uint64(index)})
elementType = elementType.StructElementTypes()[index] elementType = elementType.StructElementTypes()[index]
case llvm.ArrayTypeKind: } else {
elementType = elementType.ElementType() elementType = elementType.ElementType()
elementSize := r.targetData.TypeAllocSize(elementType) elementSize := r.targetData.TypeAllocSize(elementType)
elementSizeOperand := literalValue{elementSize} elementSizeOperand := literalValue{elementSize}
// Add operand * elementSizeOperand bytes to the pointer. // Add operand * elementSizeOperand bytes to the pointer.
inst.operands = append(inst.operands, operand, elementSizeOperand) inst.operands = append(inst.operands, operand, elementSizeOperand)
default:
// This should be unreachable.
panic("unknown type: " + elementType.String())
} }
} }
case llvm.BitCast, llvm.IntToPtr, llvm.PtrToInt: case llvm.BitCast, llvm.IntToPtr, llvm.PtrToInt:
@@ -270,12 +267,10 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
case llvm.StructTypeKind: case llvm.StructTypeKind:
offset += r.targetData.ElementOffset(indexingType, int(index)) offset += r.targetData.ElementOffset(indexingType, int(index))
indexingType = indexingType.StructElementTypes()[index] indexingType = indexingType.StructElementTypes()[index]
case llvm.ArrayTypeKind: default: // ArrayTypeKind
indexingType = indexingType.ElementType() indexingType = indexingType.ElementType()
elementSize := r.targetData.TypeAllocSize(indexingType) elementSize := r.targetData.TypeAllocSize(indexingType)
offset += elementSize * uint64(index) offset += elementSize * uint64(index)
default:
panic("unknown type kind") // unreachable
} }
} }
size := r.targetData.TypeAllocSize(inst.llvmInst.Type()) size := r.targetData.TypeAllocSize(inst.llvmInst.Type())
@@ -295,12 +290,10 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
case llvm.StructTypeKind: case llvm.StructTypeKind:
offset += r.targetData.ElementOffset(indexingType, int(index)) offset += r.targetData.ElementOffset(indexingType, int(index))
indexingType = indexingType.StructElementTypes()[index] indexingType = indexingType.StructElementTypes()[index]
case llvm.ArrayTypeKind: default: // ArrayTypeKind
indexingType = indexingType.ElementType() indexingType = indexingType.ElementType()
elementSize := r.targetData.TypeAllocSize(indexingType) elementSize := r.targetData.TypeAllocSize(indexingType)
offset += elementSize * uint64(index) offset += elementSize * uint64(index)
default:
panic("unknown type kind") // unreachable
} }
} }
// insertvalue [agg, elt, byteOffset] // insertvalue [agg, elt, byteOffset]
+6 -6
View File
@@ -127,7 +127,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
// Create a call to the package initializer (which was // Create a call to the package initializer (which was
// previously deleted). // previously deleted).
i8undef := llvm.Undef(r.i8ptrType) i8undef := llvm.Undef(r.i8ptrType)
r.builder.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{i8undef}, "") r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
// Make sure that any globals touched by the package // Make sure that any globals touched by the package
// initializer, won't be accessed by later package initializers. // initializer, won't be accessed by later package initializers.
err := r.markExternalLoad(fn) err := r.markExternalLoad(fn)
@@ -156,7 +156,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
if obj.constant { if obj.constant {
continue // constant buffers can't have been modified continue // constant buffers can't have been modified
} }
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.GlobalValueType(), &mem) initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if err == errInvalidPtrToIntSize { if err == errInvalidPtrToIntSize {
// This can happen when a previous interp run did not have the // This can happen when a previous interp run did not have the
// correct LLVM type for a global and made something up. In that // correct LLVM type for a global and made something up. In that
@@ -190,7 +190,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
if err != nil { if err != nil {
return err return err
} }
if checks && initializer.Type() != obj.llvmGlobal.GlobalValueType() { if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
panic("initializer type mismatch") panic("initializer type mismatch")
} }
obj.llvmGlobal.SetInitializer(initializer) obj.llvmGlobal.SetInitializer(initializer)
@@ -213,7 +213,7 @@ func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
r.pkgName = initName[:len(initName)-len(".init")] r.pkgName = initName[:len(initName)-len(".init")]
// Create new function with the interp result. // Create new function with the interp result.
newFn := llvm.AddFunction(mod, fn.Name()+".tmp", fn.GlobalValueType()) newFn := llvm.AddFunction(mod, fn.Name()+".tmp", fn.Type().ElementType())
newFn.SetLinkage(fn.Linkage()) newFn.SetLinkage(fn.Linkage())
newFn.SetVisibility(fn.Visibility()) newFn.SetVisibility(fn.Visibility())
entry := mod.Context().AddBasicBlock(newFn, "entry") entry := mod.Context().AddBasicBlock(newFn, "entry")
@@ -263,11 +263,11 @@ func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
if obj.constant { if obj.constant {
continue // constant, so can't have been modified continue // constant, so can't have been modified
} }
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.GlobalValueType(), &mem) initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if err != nil { if err != nil {
return err return err
} }
if checks && initializer.Type() != obj.llvmGlobal.GlobalValueType() { if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
panic("initializer type mismatch") panic("initializer type mismatch")
} }
obj.llvmGlobal.SetInitializer(initializer) obj.llvmGlobal.SetInitializer(initializer)
+49 -70
View File
@@ -356,7 +356,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
default: default:
panic("unknown integer type width") panic("unknown integer type width")
} }
case strings.HasPrefix(callFn.name, "llvm.memcpy.p0") || strings.HasPrefix(callFn.name, "llvm.memmove.p0"): case strings.HasPrefix(callFn.name, "llvm.memcpy.p0i8.p0i8.") || strings.HasPrefix(callFn.name, "llvm.memmove.p0i8.p0i8."):
// Copy a block of memory from one pointer to another. // Copy a block of memory from one pointer to another.
dst, err := operands[1].asPointer(r) dst, err := operands[1].asPointer(r)
if err != nil { if err != nil {
@@ -408,7 +408,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// Elem() is only valid for certain type classes. // Elem() is only valid for certain type classes.
switch class { switch class {
case "chan", "pointer", "slice", "array": case "chan", "pointer", "slice", "array":
elementType := r.builder.CreateExtractValue(typecodeID.Initializer(), 0, "") elementType := llvm.ConstExtractValue(typecodeID.Initializer(), []uint32{0})
uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8) uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8)
locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType)) locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType))
default: default:
@@ -461,8 +461,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// easier checking in the next step. // easier checking in the next step.
concreteTypeMethods := map[string]struct{}{} concreteTypeMethods := map[string]struct{}{}
for i := 0; i < methodSet.Type().ArrayLength(); i++ { for i := 0; i < methodSet.Type().ArrayLength(); i++ {
methodInfo := r.builder.CreateExtractValue(methodSet, i, "") methodInfo := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
name := r.builder.CreateExtractValue(methodInfo, 0, "").Name() name := llvm.ConstExtractValue(methodInfo, []uint32{0}).Name()
concreteTypeMethods[name] = struct{}{} concreteTypeMethods[name] = struct{}{}
} }
@@ -496,7 +496,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
typecodeID := typecodeIDBitCast.Operand(0).Initializer() typecodeID := typecodeIDBitCast.Operand(0).Initializer()
// Load the method set, which is part of the typecodeID object. // Load the method set, which is part of the typecodeID object.
methodSet := stripPointerCasts(r.builder.CreateExtractValue(typecodeID, 2, "")).Initializer() methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
// We don't need to load the interface method set. // We don't need to load the interface method set.
@@ -511,10 +511,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
numMethods := methodSet.Type().ArrayLength() numMethods := methodSet.Type().ArrayLength()
var method llvm.Value var method llvm.Value
for i := 0; i < numMethods; i++ { for i := 0; i < numMethods; i++ {
methodSignatureAgg := r.builder.CreateExtractValue(methodSet, i, "") methodSignature := llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 0})
methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, 0, "")
if methodSignature == signature { if methodSignature == signature {
method = r.builder.CreateExtractValue(methodSignatureAgg, 1, "").Operand(0) method = llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 1}).Operand(0)
} }
} }
if method.IsNil() { if method.IsNil() {
@@ -637,7 +636,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// Create the new object. // Create the new object.
size := operands[0].(literalValue).value.(uint64) size := operands[0].(literalValue).value.(uint64)
alloca := object{ alloca := object{
llvmType: inst.llvmInst.AllocatedType(), llvmType: inst.llvmInst.Type(),
globalName: r.pkgName + "$alloca", globalName: r.pkgName + "$alloca",
buffer: newRawValue(uint32(size)), buffer: newRawValue(uint32(size)),
size: uint32(size), size: uint32(size),
@@ -655,7 +654,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// GetElementPtr does pointer arithmetic, changing the offset of the // GetElementPtr does pointer arithmetic, changing the offset of the
// pointer into the underlying object. // pointer into the underlying object.
var offset uint64 var offset uint64
for i := 1; i < len(operands); i += 2 { for i := 2; i < len(operands); i += 2 {
index := operands[i].Uint() index := operands[i].Uint()
elementSize := operands[i+1].Uint() elementSize := operands[i+1].Uint()
if int64(elementSize) < 0 { if int64(elementSize) < 0 {
@@ -723,9 +722,46 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
locals[inst.localIndex] = newagg locals[inst.localIndex] = newagg
case llvm.ICmp: case llvm.ICmp:
predicate := llvm.IntPredicate(operands[2].(literalValue).value.(uint8)) predicate := llvm.IntPredicate(operands[2].(literalValue).value.(uint8))
var result bool
lhs := operands[0] lhs := operands[0]
rhs := operands[1] rhs := operands[1]
result := r.interpretICmp(lhs, rhs, predicate) switch predicate {
case llvm.IntEQ, llvm.IntNE:
lhsPointer, lhsErr := lhs.asPointer(r)
rhsPointer, rhsErr := rhs.asPointer(r)
if (lhsErr == nil) != (rhsErr == nil) {
// Fast path: only one is a pointer, so they can't be equal.
result = false
} else if lhsErr == nil {
// Both must be nil, so both are pointers.
// Compare them directly.
result = lhsPointer.equal(rhsPointer)
} else {
// Fall back to generic comparison.
result = lhs.asRawValue(r).equal(rhs.asRawValue(r))
}
if predicate == llvm.IntNE {
result = !result
}
case llvm.IntUGT:
result = lhs.Uint() > rhs.Uint()
case llvm.IntUGE:
result = lhs.Uint() >= rhs.Uint()
case llvm.IntULT:
result = lhs.Uint() < rhs.Uint()
case llvm.IntULE:
result = lhs.Uint() <= rhs.Uint()
case llvm.IntSGT:
result = lhs.Int() > rhs.Int()
case llvm.IntSGE:
result = lhs.Int() >= rhs.Int()
case llvm.IntSLT:
result = lhs.Int() < rhs.Int()
case llvm.IntSLE:
result = lhs.Int() <= rhs.Int()
default:
return nil, mem, r.errorAt(inst, errors.New("interp: unsupported icmp"))
}
if result { if result {
locals[inst.localIndex] = literalValue{uint8(1)} locals[inst.localIndex] = literalValue{uint8(1)}
} else { } else {
@@ -911,51 +947,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator")) return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
} }
// Interpret an icmp instruction. Doesn't have side effects, only returns the
// output of the comparison.
func (r *runner) interpretICmp(lhs, rhs value, predicate llvm.IntPredicate) bool {
switch predicate {
case llvm.IntEQ, llvm.IntNE:
var result bool
lhsPointer, lhsErr := lhs.asPointer(r)
rhsPointer, rhsErr := rhs.asPointer(r)
if (lhsErr == nil) != (rhsErr == nil) {
// Fast path: only one is a pointer, so they can't be equal.
result = false
} else if lhsErr == nil {
// Both must be nil, so both are pointers.
// Compare them directly.
result = lhsPointer.equal(rhsPointer)
} else {
// Fall back to generic comparison.
result = lhs.asRawValue(r).equal(rhs.asRawValue(r))
}
if predicate == llvm.IntNE {
result = !result
}
return result
case llvm.IntUGT:
return lhs.Uint() > rhs.Uint()
case llvm.IntUGE:
return lhs.Uint() >= rhs.Uint()
case llvm.IntULT:
return lhs.Uint() < rhs.Uint()
case llvm.IntULE:
return lhs.Uint() <= rhs.Uint()
case llvm.IntSGT:
return lhs.Int() > rhs.Int()
case llvm.IntSGE:
return lhs.Int() >= rhs.Int()
case llvm.IntSLT:
return lhs.Int() < rhs.Int()
case llvm.IntSLE:
return lhs.Int() <= rhs.Int()
default:
// _should_ be unreachable, until LLVM adds new icmp operands (unlikely)
panic("interp: unsupported icmp")
}
}
func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error { func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error {
numOperands := inst.llvmInst.OperandsCount() numOperands := inst.llvmInst.OperandsCount()
operands := make([]llvm.Value, numOperands) operands := make([]llvm.Value, numOperands)
@@ -986,13 +977,13 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
} }
} }
} }
result = r.builder.CreateCall(inst.llvmInst.CalledFunctionType(), llvmFn, args, inst.name) result = r.builder.CreateCall(llvmFn, args, inst.name)
case llvm.Load: case llvm.Load:
err := mem.markExternalLoad(operands[0]) err := mem.markExternalLoad(operands[0])
if err != nil { if err != nil {
return r.errorAt(inst, err) return r.errorAt(inst, err)
} }
result = r.builder.CreateLoad(inst.llvmInst.Type(), operands[0], inst.name) result = r.builder.CreateLoad(operands[0], inst.name)
if inst.llvmInst.IsVolatile() { if inst.llvmInst.IsVolatile() {
result.SetVolatile(true) result.SetVolatile(true)
} }
@@ -1095,15 +1086,3 @@ func intPredicateString(predicate llvm.IntPredicate) string {
return "cmp?" return "cmp?"
} }
} }
// Strip some pointer casts. This is probably unnecessary once support for
// LLVM 14 (non-opaque pointers) is dropped.
func stripPointerCasts(value llvm.Value) llvm.Value {
if !value.IsAConstantExpr().IsNil() {
switch value.Opcode() {
case llvm.GetElementPtr, llvm.BitCast:
return stripPointerCasts(value.Operand(0))
}
}
return value
}
+18 -44
View File
@@ -37,7 +37,7 @@ import (
// ability to roll back interpreting a function. // ability to roll back interpreting a function.
type object struct { type object struct {
llvmGlobal llvm.Value llvmGlobal llvm.Value
llvmType llvm.Type // must match llvmGlobal.GlobalValueType() if both are set, may be unset if llvmGlobal is set llvmType llvm.Type // must match llvmGlobal.Type() if both are set, may be unset if llvmGlobal is set
llvmLayoutType llvm.Type // LLVM type based on runtime.alloc layout parameter, if available llvmLayoutType llvm.Type // LLVM type based on runtime.alloc layout parameter, if available
globalName string // name, if not yet created (not guaranteed to be the final name) globalName string // name, if not yet created (not guaranteed to be the final name)
buffer value // buffer with value as given by interp, nil if external buffer value // buffer with value as given by interp, nil if external
@@ -215,7 +215,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
case llvm.StructTypeKind: case llvm.StructTypeKind:
numElements := llvmType.StructElementTypesCount() numElements := llvmType.StructElementTypesCount()
for i := 0; i < numElements; i++ { for i := 0; i < numElements; i++ {
element := mv.r.builder.CreateExtractValue(llvmValue, i, "") element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
err := mv.markExternal(element, mark) err := mv.markExternal(element, mark)
if err != nil { if err != nil {
return err return err
@@ -224,7 +224,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
case llvm.ArrayTypeKind: case llvm.ArrayTypeKind:
numElements := llvmType.ArrayLength() numElements := llvmType.ArrayLength()
for i := 0; i < numElements; i++ { for i := 0; i < numElements; i++ {
element := mv.r.builder.CreateExtractValue(llvmValue, i, "") element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
err := mv.markExternal(element, mark) err := mv.markExternal(element, mark)
if err != nil { if err != nil {
return err return err
@@ -594,7 +594,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
var globalType llvm.Type var globalType llvm.Type
if !obj.llvmType.IsNil() { if !obj.llvmType.IsNil() {
// The exact type is known. // The exact type is known.
globalType = obj.llvmType globalType = obj.llvmType.ElementType()
} else { // !obj.llvmLayoutType.IsNil() } else { // !obj.llvmLayoutType.IsNil()
// The exact type isn't known, but the object layout is known. // The exact type isn't known, but the object layout is known.
globalType = obj.llvmLayoutType globalType = obj.llvmLayoutType
@@ -646,8 +646,8 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
if llvmValue.Type() != mem.r.i8ptrType { if llvmValue.Type() != mem.r.i8ptrType {
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType) llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
} }
llvmValue = llvm.ConstInBoundsGEP(mem.r.mod.Context().Int8Type(), llvmValue, []llvm.Value{ llvmValue = llvm.ConstInBoundsGEP(llvmValue, []llvm.Value{
llvm.ConstInt(mem.r.mod.Context().Int32Type(), uint64(v.offset()), false), llvm.ConstInt(llvmValue.Type().Context().Int32Type(), uint64(v.offset()), false),
}) })
} }
@@ -808,17 +808,14 @@ func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
if !field.IsAGlobalVariable().IsNil() { elementType := field.Type().ElementType()
elementType := field.GlobalValueType() if elementType.TypeKind() == llvm.StructTypeKind {
if elementType.TypeKind() == llvm.StructTypeKind { // There are some special pointer types that should be used as a
// There are some special pointer types that should be used // ptrtoint, so that they can be used in certain optimizations.
// as a ptrtoint, so that they can be used in certain name := elementType.StructName()
// optimizations. if name == "runtime.typecodeID" || name == "runtime.funcValueWithSignature" {
name := elementType.StructName() uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
if name == "runtime.typecodeID" || name == "runtime.funcValueWithSignature" { field = llvm.ConstPtrToInt(field, uintptrType)
uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
field = llvm.ConstPtrToInt(field, uintptrType)
}
} }
} }
structFields = append(structFields, field) structFields = append(structFields, field)
@@ -1001,7 +998,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
ptr := llvmValue.Operand(0) ptr := llvmValue.Operand(0)
index := llvmValue.Operand(1) index := llvmValue.Operand(1)
numOperands := llvmValue.OperandsCount() numOperands := llvmValue.OperandsCount()
elementType := llvmValue.GEPSourceElementType() elementType := ptr.Type().ElementType()
totalOffset := r.targetData.TypeAllocSize(elementType) * index.ZExtValue() totalOffset := r.targetData.TypeAllocSize(elementType) * index.ZExtValue()
for i := 2; i < numOperands; i++ { for i := 2; i < numOperands; i++ {
indexValue := llvmValue.Operand(i) indexValue := llvmValue.Operand(i)
@@ -1031,17 +1028,6 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
for i := uint32(0); i < ptrSize; i++ { for i := uint32(0); i < ptrSize; i++ {
v.buf[i] = ptrValue.pointer v.buf[i] = ptrValue.pointer
} }
case llvm.ICmp:
size := r.targetData.TypeAllocSize(llvmValue.Operand(0).Type())
lhs := newRawValue(uint32(size))
rhs := newRawValue(uint32(size))
lhs.set(llvmValue.Operand(0), r)
rhs.set(llvmValue.Operand(1), r)
if r.interpretICmp(lhs, rhs, llvmValue.IntPredicate()) {
v.buf[0] = 1 // result is true
} else {
v.buf[0] = 0 // result is false
}
default: default:
llvmValue.Dump() llvmValue.Dump()
println() println()
@@ -1088,7 +1074,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
field := rawValue{ field := rawValue{
buf: v.buf[offset:], buf: v.buf[offset:],
} }
field.set(r.builder.CreateExtractValue(llvmValue, i, ""), r) field.set(llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)}), r)
} }
case llvm.ArrayTypeKind: case llvm.ArrayTypeKind:
numElements := llvmType.ArrayLength() numElements := llvmType.ArrayLength()
@@ -1099,7 +1085,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
field := rawValue{ field := rawValue{
buf: v.buf[offset:], buf: v.buf[offset:],
} }
field.set(r.builder.CreateExtractValue(llvmValue, i, ""), r) field.set(llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)}), r)
} }
case llvm.DoubleTypeKind: case llvm.DoubleTypeKind:
f, _ := llvmValue.DoubleValue() f, _ := llvmValue.DoubleValue()
@@ -1187,7 +1173,7 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
r.globals[llvmValue] = index r.globals[llvmValue] = index
r.objects = append(r.objects, obj) r.objects = append(r.objects, obj)
if !llvmValue.IsAGlobalVariable().IsNil() { if !llvmValue.IsAGlobalVariable().IsNil() {
obj.size = uint32(r.targetData.TypeAllocSize(llvmValue.GlobalValueType())) obj.size = uint32(r.targetData.TypeAllocSize(llvmValue.Type().ElementType()))
if initializer := llvmValue.Initializer(); !initializer.IsNil() { if initializer := llvmValue.Initializer(); !initializer.IsNil() {
obj.buffer = r.getValue(initializer) obj.buffer = r.getValue(initializer)
obj.constant = llvmValue.IsGlobalConstant() obj.constant = llvmValue.IsGlobalConstant()
@@ -1236,8 +1222,6 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
// readObjectLayout reads the object layout as it is stored by the compiler. It // readObjectLayout reads the object layout as it is stored by the compiler. It
// returns the size in the number of words and the bitmap. // returns the size in the number of words and the bitmap.
//
// For details on this format, see src/runtime/gc_precise.go.
func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) { func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
pointerSize := layoutValue.len(r) pointerSize := layoutValue.len(r)
if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.i8ptrType) { if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.i8ptrType) {
@@ -1291,7 +1275,6 @@ func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
} }
rawBytes[i] = byte(v) rawBytes[i] = byte(v)
} }
reverseBytes(rawBytes) // little-endian to big-endian
bitmap := new(big.Int).SetBytes(rawBytes) bitmap := new(big.Int).SetBytes(rawBytes)
return objectSizeWords, bitmap return objectSizeWords, bitmap
} }
@@ -1341,12 +1324,3 @@ func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type {
} }
return llvmLayoutType return llvmLayoutType
} }
// Reverse a slice of bytes. From the wiki:
// https://github.com/golang/go/wiki/SliceTricks#reversing
func reverseBytes(buf []byte) {
for i := len(buf)/2 - 1; i >= 0; i-- {
opp := len(buf) - 1 - i
buf[i], buf[opp] = buf[opp], buf[i]
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32--wasi" target triple = "wasm32--wasi"
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " } @"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c" \00\00\00\00\00\00\01" }
@pointerFree12 = global i8* null @pointerFree12 = global i8* null
@pointerFree7 = global i8* null @pointerFree7 = global i8* null
@pointerFree3 = global i8* null @pointerFree3 = global i8* null
-15
View File
@@ -3,7 +3,6 @@ target triple = "x86_64--linux"
@intToPtrResult = global i8 0 @intToPtrResult = global i8 0
@ptrToIntResult = global i8 0 @ptrToIntResult = global i8 0
@icmpResult = global i8 0
@someArray = internal global {i16, i8, i8} zeroinitializer @someArray = internal global {i16, i8, i8} zeroinitializer
@someArrayPointer = global i8* zeroinitializer @someArrayPointer = global i8* zeroinitializer
@@ -16,7 +15,6 @@ define internal void @main.init() {
call void @testIntToPtr() call void @testIntToPtr()
call void @testPtrToInt() call void @testPtrToInt()
call void @testConstGEP() call void @testConstGEP()
call void @testICmp()
ret void ret void
} }
@@ -50,16 +48,3 @@ define internal void @testConstGEP() {
store i8* getelementptr inbounds (i8, i8* bitcast ({i16, i8, i8}* @someArray to i8*), i32 2), i8** @someArrayPointer store i8* getelementptr inbounds (i8, i8* bitcast ({i16, i8, i8}* @someArray to i8*), i32 2), i8** @someArrayPointer
ret void ret void
} }
define internal void @testICmp() {
br i1 icmp eq (i64 ptrtoint (i8* @ptrToIntResult to i64), i64 0), label %equal, label %unequal
equal:
; should not be reached
store i8 1, i8* @icmpResult
ret void
unequal:
; should be reached
store i8 2, i8* @icmpResult
ret void
ret void
}
-1
View File
@@ -3,7 +3,6 @@ target triple = "x86_64--linux"
@intToPtrResult = local_unnamed_addr global i8 2 @intToPtrResult = local_unnamed_addr global i8 2
@ptrToIntResult = local_unnamed_addr global i8 2 @ptrToIntResult = local_unnamed_addr global i8 2
@icmpResult = local_unnamed_addr global i8 2
@someArray = internal global { i16, i8, i8 } zeroinitializer @someArray = internal global { i16, i8, i8 } zeroinitializer
@someArrayPointer = local_unnamed_addr global i8* getelementptr inbounds ({ i16, i8, i8 }, { i16, i8, i8 }* @someArray, i64 0, i32 1) @someArrayPointer = local_unnamed_addr global i8* getelementptr inbounds ({ i16, i8, i8 }, { i16, i8, i8 }* @someArray, i64 0, i32 1)
+383 -435
View File
@@ -155,27 +155,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
return nil return nil
} }
// Create a temporary directory for intermediary files. return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
tmpdir, err := os.MkdirTemp("", "tinygo")
if err != nil {
return err
}
if !options.Work {
defer os.RemoveAll(tmpdir)
}
// Do the build.
result, err := builder.Build(pkgName, outpath, tmpdir, config)
if err != nil {
return err
}
if result.Binary != "" {
// If result.Binary is set, it means there is a build output (elf, hex,
// etc) that we need to move to the outpath. If it isn't set, it means
// the build output was a .ll, .bc or .o file that has already been
// written to outpath and so we don't need to do anything.
if outpath == "" { if outpath == "" {
if strings.HasSuffix(pkgName, ".go") { if strings.HasSuffix(pkgName, ".go") {
// A Go file was specified directly on the command line. // A Go file was specified directly on the command line.
@@ -207,11 +187,11 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
// Check whether file writing was successful. // Check whether file writing was successful.
return outf.Close() return outf.Close()
} else {
// Move was successful.
return nil
} }
} })
// Move was successful.
return nil
} }
// Test runs the tests in the given package. Returns whether the test passed and // Test runs the tests in the given package. Returns whether the test passed and
@@ -244,10 +224,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
flags = append(flags, "-test.benchmem") flags = append(flags, "-test.benchmem")
} }
buf := bytes.Buffer{}
passed := false passed := false
var duration time.Duration err = buildAndRun(pkgName, config, os.Stdout, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
result, err := buildAndRun(pkgName, config, &buf, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
if testCompileOnly || outpath != "" { if testCompileOnly || outpath != "" {
// Write test binary to the specified file name. // Write test binary to the specified file name.
if outpath == "" { if outpath == "" {
@@ -286,13 +264,24 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
// relative directory up to the module root, even if the test never // relative directory up to the module root, even if the test never
// reads any files. // reads any files.
// //
// Ex. run --dir=.. --dir=../.. --dir=../../.. // Ex. --dir=.. --dir=../.. --dir=../../..
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot) dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
args := []string{"run"} var args []string
for _, d := range dirs[1:] { for _, d := range dirs[1:] {
args = append(args, "--dir="+d) args = append(args, "--dir="+d)
} }
// Some tests create temp directories using os.MkdirTemp or via
// t.TempDir(). Create a writeable directory and map it to the
// default tempDir environment variable: TMPDIR.
tmpdir, err := os.MkdirTemp("", "tinygotmp")
if err != nil {
return fmt.Errorf("failed to create temporary directory: %w", err)
}
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
// TODO: add option to not delete temp dir for debugging?
defer os.RemoveAll(tmpdir)
// The below re-organizes the arguments so that the current // The below re-organizes the arguments so that the current
// directory is added last. // directory is added last.
args = append(args, cmd.Args[1:]...) args = append(args, cmd.Args[1:]...)
@@ -302,34 +291,27 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
// Run the test. // Run the test.
start := time.Now() start := time.Now()
err = cmd.Run() err = cmd.Run()
duration = time.Since(start) duration := time.Since(start)
// Print the result.
importPath := strings.TrimSuffix(result.ImportPath, ".test")
passed = err == nil passed = err == nil
if passed {
// print the test output if fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
// 1) the tests passed and in verbose mode } else {
// 2) the tests failed fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
// 3) running benchmarks
if (passed && testVerbose) || (!passed) || (testBenchRegexp != "") {
buf.WriteTo(stdout)
} }
if _, ok := err.(*exec.ExitError); ok { if _, ok := err.(*exec.ExitError); ok {
// Binary exited with a non-zero exit code, which means the test // Binary exited with a non-zero exit code, which means the test
// failed. Return nil to avoid printing a useless "exited with // failed.
// error" error message.
return nil return nil
} }
return err return err
}) })
importPath := strings.TrimSuffix(result.ImportPath, ".test")
if err, ok := err.(loader.NoTestFilesError); ok { if err, ok := err.(loader.NoTestFilesError); ok {
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath) fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath)
// Pretend the test passed - it at least didn't fail. // Pretend the test passed - it at least didn't fail.
return true, nil return true, nil
} else if passed {
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
} else {
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
} }
return passed, err return passed, err
} }
@@ -389,130 +371,116 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return errors.New("unknown flash method: " + flashMethod) return errors.New("unknown flash method: " + flashMethod)
} }
// Create a temporary directory for intermediary files. return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
tmpdir, err := os.MkdirTemp("", "tinygo") // do we need port reset to put MCU into bootloader mode?
if err != nil { if config.Target.PortReset == "true" && flashMethod != "openocd" {
return err port, err := getDefaultPort(port, config.Target.SerialPort)
} if err == nil {
if !options.Work { err = touchSerialPortAt1200bps(port)
defer os.RemoveAll(tmpdir) if err != nil {
} return &commandError{"failed to reset port", result.Binary, err}
}
// Build the binary. // give the target MCU a chance to restart into bootloader
result, err := builder.Build(pkgName, fileExt, tmpdir, config) time.Sleep(3 * time.Second)
if err != nil {
return err
}
// do we need port reset to put MCU into bootloader mode?
if config.Target.PortReset == "true" && flashMethod != "openocd" {
port, err := getDefaultPort(port, config.Target.SerialPort)
if err == nil {
err = touchSerialPortAt1200bps(port)
if err != nil {
return &commandError{"failed to reset port", port, err}
}
// give the target MCU a chance to restart into bootloader
time.Sleep(3 * time.Second)
}
}
// Flash the binary to the MCU.
switch flashMethod {
case "", "command":
// Create the command.
flashCmd := config.Target.FlashCommand
flashCmdList, err := shlex.Split(flashCmd)
if err != nil {
return fmt.Errorf("could not parse flash command %#v: %w", flashCmd, err)
}
if strings.Contains(flashCmd, "{port}") {
var err error
port, err = getDefaultPort(port, config.Target.SerialPort)
if err != nil {
return err
} }
} }
// Fill in fields in the command template.
fileToken := "{" + fileExt[1:] + "}"
for i, arg := range flashCmdList {
arg = strings.ReplaceAll(arg, fileToken, result.Binary)
arg = strings.ReplaceAll(arg, "{port}", port)
flashCmdList[i] = arg
}
// Execute the command.
if len(flashCmdList) < 2 {
return fmt.Errorf("invalid flash command: %#v", flashCmd)
}
cmd := executeCommand(config.Options, flashCmdList[0], flashCmdList[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = goenv.Get("TINYGOROOT")
err = cmd.Run()
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
case "msd":
// this flashing method copies the binary data to a Mass Storage Device (msd) // this flashing method copies the binary data to a Mass Storage Device (msd)
switch fileExt { switch flashMethod {
case ".uf2": case "", "command":
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary, config.Options) // Create the command.
flashCmd := config.Target.FlashCommand
flashCmdList, err := shlex.Split(flashCmd)
if err != nil {
return fmt.Errorf("could not parse flash command %#v: %w", flashCmd, err)
}
if strings.Contains(flashCmd, "{port}") {
var err error
port, err = getDefaultPort(port, config.Target.SerialPort)
if err != nil {
return err
}
}
// Fill in fields in the command template.
fileToken := "{" + fileExt[1:] + "}"
for i, arg := range flashCmdList {
arg = strings.ReplaceAll(arg, fileToken, result.Binary)
arg = strings.ReplaceAll(arg, "{port}", port)
flashCmdList[i] = arg
}
// Execute the command.
if len(flashCmdList) < 2 {
return fmt.Errorf("invalid flash command: %#v", flashCmd)
}
cmd := executeCommand(config.Options, flashCmdList[0], flashCmdList[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = goenv.Get("TINYGOROOT")
err = cmd.Run()
if err != nil { if err != nil {
return &commandError{"failed to flash", result.Binary, err} return &commandError{"failed to flash", result.Binary, err}
} }
case ".hex": case "msd":
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary, config.Options) switch fileExt {
case ".uf2":
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
case ".hex":
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
default:
return errors.New("mass storage device flashing currently only supports uf2 and hex")
}
case "openocd":
args, err := config.OpenOCDConfiguration()
if err != nil {
return err
}
exit := " reset exit"
if config.Target.OpenOCDVerify != nil && *config.Target.OpenOCDVerify {
exit = " verify" + exit
}
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+exit)
cmd := executeCommand(config.Options, "openocd", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
case "bmp":
gdb, err := config.Target.LookupGDB()
if err != nil {
return err
}
var bmpGDBPort string
bmpGDBPort, _, err = getBMPPorts()
if err != nil {
return err
}
args := []string{"-ex", "target extended-remote " + bmpGDBPort, "-ex", "monitor swdp_scan", "-ex", "attach 1", "-ex", "load", filepath.ToSlash(result.Binary)}
cmd := executeCommand(config.Options, gdb, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil { if err != nil {
return &commandError{"failed to flash", result.Binary, err} return &commandError{"failed to flash", result.Binary, err}
} }
default: default:
return errors.New("mass storage device flashing currently only supports uf2 and hex") return fmt.Errorf("unknown flash method: %s", flashMethod)
} }
case "openocd": if options.Monitor {
args, err := config.OpenOCDConfiguration() return Monitor("", options)
if err != nil {
return err
} }
exit := " reset exit" return nil
if config.Target.OpenOCDVerify != nil && *config.Target.OpenOCDVerify { })
exit = " verify" + exit
}
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+exit)
cmd := executeCommand(config.Options, "openocd", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
case "bmp":
gdb, err := config.Target.LookupGDB()
if err != nil {
return err
}
var bmpGDBPort string
bmpGDBPort, _, err = getBMPPorts()
if err != nil {
return err
}
args := []string{"-ex", "target extended-remote " + bmpGDBPort, "-ex", "monitor swdp_scan", "-ex", "attach 1", "-ex", "load", filepath.ToSlash(result.Binary)}
cmd := executeCommand(config.Options, gdb, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
default:
return fmt.Errorf("unknown flash method: %s", flashMethod)
}
if options.Monitor {
return Monitor("", options)
}
return nil
} }
// Debug compiles and flashes a program to a microcontroller (just like Flash) // Debug compiles and flashes a program to a microcontroller (just like Flash)
@@ -538,208 +506,195 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
return err return err
} }
// Create a temporary directory for intermediary files.
tmpdir, err := os.MkdirTemp("", "tinygo")
if err != nil {
return err
}
if !options.Work {
defer os.RemoveAll(tmpdir)
}
// Build the binary to debug.
format, fileExt := config.EmulatorFormat() format, fileExt := config.EmulatorFormat()
result, err := builder.Build(pkgName, fileExt, tmpdir, config) return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
if err != nil { // Find a good way to run GDB.
return err gdbInterface, openocdInterface := config.Programmer()
} switch gdbInterface {
case "msd", "command", "":
// Find a good way to run GDB. emulator := config.EmulatorName()
gdbInterface, openocdInterface := config.Programmer() if emulator != "" {
switch gdbInterface { if emulator == "mgba" {
case "msd", "command", "": gdbInterface = "mgba"
emulator := config.EmulatorName() } else if emulator == "simavr" {
if emulator != "" { gdbInterface = "simavr"
if emulator == "mgba" { } else if strings.HasPrefix(emulator, "qemu-system-") {
gdbInterface = "mgba" gdbInterface = "qemu"
} else if emulator == "simavr" { } else {
gdbInterface = "simavr" // Assume QEMU as an emulator.
} else if strings.HasPrefix(emulator, "qemu-system-") { gdbInterface = "qemu-user"
gdbInterface = "qemu"
} else {
// Assume QEMU as an emulator.
gdbInterface = "qemu-user"
}
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
gdbInterface = "openocd"
} else if config.Target.JLinkDevice != "" {
gdbInterface = "jlink"
} else {
gdbInterface = "native"
}
}
// Run the GDB server, if necessary.
port := ""
var gdbCommands []string
var daemon *exec.Cmd
emulator, err := config.Emulator(format, result.Binary)
if err != nil {
return err
}
switch gdbInterface {
case "native":
// Run GDB directly.
case "bmp":
var bmpGDBPort string
bmpGDBPort, _, err = getBMPPorts()
if err != nil {
return err
}
port = bmpGDBPort
gdbCommands = append(gdbCommands, "monitor swdp_scan", "compare-sections", "attach 1", "load")
case "openocd":
port = ":3333"
gdbCommands = append(gdbCommands, "monitor halt", "load", "monitor reset halt")
// We need a separate debugging daemon for on-chip debugging.
args, err := config.OpenOCDConfiguration()
if err != nil {
return err
}
daemon = executeCommand(config.Options, "openocd", args...)
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
Out: colorable.NewColorableStderr(),
Prefix: "openocd: ",
Color: TermColorYellow,
}
daemon.Stdout = w
daemon.Stderr = w
}
case "jlink":
port = ":2331"
gdbCommands = append(gdbCommands, "load", "monitor reset halt")
// We need a separate debugging daemon for on-chip debugging.
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
Out: colorable.NewColorableStderr(),
Prefix: "jlink: ",
Color: TermColorYellow,
}
daemon.Stdout = w
daemon.Stderr = w
}
case "qemu":
port = ":1234"
// Run in an emulator.
args := append(emulator[1:], "-s", "-S")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "qemu-user":
port = ":1234"
// Run in an emulator.
args := append(emulator[1:], "-g", "1234")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "mgba":
port = ":2345"
// Run in an emulator.
args := append(emulator[1:], "-g")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "simavr":
port = ":1234"
// Run in an emulator.
args := append(emulator[1:], "-g")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "msd":
return errors.New("gdb is not supported for drag-and-drop programmable devices")
default:
return fmt.Errorf("gdb is not supported with interface %#v", gdbInterface)
}
if daemon != nil {
// Make sure the daemon doesn't receive Ctrl-C that is intended for
// GDB (to break the currently executing program).
setCommandAsDaemon(daemon)
// Start now, and kill it on exit.
err = daemon.Start()
if err != nil {
return &commandError{"failed to run", daemon.Path, err}
}
defer func() {
daemon.Process.Signal(os.Interrupt)
var stopped uint32
go func() {
time.Sleep(time.Millisecond * 100)
if atomic.LoadUint32(&stopped) == 0 {
daemon.Process.Kill()
} }
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
gdbInterface = "openocd"
} else if config.Target.JLinkDevice != "" {
gdbInterface = "jlink"
} else {
gdbInterface = "native"
}
}
// Run the GDB server, if necessary.
port := ""
var gdbCommands []string
var daemon *exec.Cmd
emulator, err := config.Emulator(format, result.Binary)
if err != nil {
return err
}
switch gdbInterface {
case "native":
// Run GDB directly.
case "bmp":
var bmpGDBPort string
bmpGDBPort, _, err = getBMPPorts()
if err != nil {
return err
}
port = bmpGDBPort
gdbCommands = append(gdbCommands, "monitor swdp_scan", "compare-sections", "attach 1", "load")
case "openocd":
port = ":3333"
gdbCommands = append(gdbCommands, "monitor halt", "load", "monitor reset halt")
// We need a separate debugging daemon for on-chip debugging.
args, err := config.OpenOCDConfiguration()
if err != nil {
return err
}
daemon = executeCommand(config.Options, "openocd", args...)
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
Out: colorable.NewColorableStderr(),
Prefix: "openocd: ",
Color: TermColorYellow,
}
daemon.Stdout = w
daemon.Stderr = w
}
case "jlink":
port = ":2331"
gdbCommands = append(gdbCommands, "load", "monitor reset halt")
// We need a separate debugging daemon for on-chip debugging.
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
Out: colorable.NewColorableStderr(),
Prefix: "jlink: ",
Color: TermColorYellow,
}
daemon.Stdout = w
daemon.Stderr = w
}
case "qemu":
port = ":1234"
// Run in an emulator.
args := append(emulator[1:], "-s", "-S")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "qemu-user":
port = ":1234"
// Run in an emulator.
args := append(emulator[1:], "-g", "1234")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "mgba":
port = ":2345"
// Run in an emulator.
args := append(emulator[1:], "-g")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "simavr":
port = ":1234"
// Run in an emulator.
args := append(emulator[1:], "-g")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "msd":
return errors.New("gdb is not supported for drag-and-drop programmable devices")
default:
return fmt.Errorf("gdb is not supported with interface %#v", gdbInterface)
}
if daemon != nil {
// Make sure the daemon doesn't receive Ctrl-C that is intended for
// GDB (to break the currently executing program).
setCommandAsDaemon(daemon)
// Start now, and kill it on exit.
err = daemon.Start()
if err != nil {
return &commandError{"failed to run", daemon.Path, err}
}
defer func() {
daemon.Process.Signal(os.Interrupt)
var stopped uint32
go func() {
time.Sleep(time.Millisecond * 100)
if atomic.LoadUint32(&stopped) == 0 {
daemon.Process.Kill()
}
}()
daemon.Wait()
atomic.StoreUint32(&stopped, 1)
}() }()
daemon.Wait() }
atomic.StoreUint32(&stopped, 1)
// Ignore Ctrl-C, it must be passed on to GDB.
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
}
}() }()
}
// Ignore Ctrl-C, it must be passed on to GDB. // Construct and execute a gdb or lldb command.
c := make(chan os.Signal, 1) // By default: gdb -ex run <binary>
signal.Notify(c, os.Interrupt) // Exit the debugger with Ctrl-D.
go func() { params := []string{result.Executable}
for range c { switch debugger {
} case "gdb":
}() if port != "" {
params = append(params, "-ex", "target extended-remote "+port)
// Construct and execute a gdb or lldb command. }
// By default: gdb -ex run <binary> for _, cmd := range gdbCommands {
// Exit the debugger with Ctrl-D. params = append(params, "-ex", cmd)
params := []string{result.Executable} }
switch debugger { case "lldb":
case "gdb": params = append(params, "--arch", config.Triple())
if port != "" { if port != "" {
params = append(params, "-ex", "target extended-remote "+port) if strings.HasPrefix(port, ":") {
} params = append(params, "-o", "gdb-remote "+port[1:])
for _, cmd := range gdbCommands { } else {
params = append(params, "-ex", cmd) return fmt.Errorf("cannot use LLDB over a gdb-remote that isn't a TCP port: %s", port)
} }
case "lldb": }
params = append(params, "--arch", config.Triple()) for _, cmd := range gdbCommands {
if port != "" { if strings.HasPrefix(cmd, "monitor ") {
if strings.HasPrefix(port, ":") { params = append(params, "-o", "process plugin packet "+cmd)
params = append(params, "-o", "gdb-remote "+port[1:]) } else if cmd == "load" {
} else { params = append(params, "-o", "target modules load --load --slide 0")
return fmt.Errorf("cannot use LLDB over a gdb-remote that isn't a TCP port: %s", port) } else {
return fmt.Errorf("don't know how to convert GDB command %#v to LLDB", cmd)
}
} }
} }
for _, cmd := range gdbCommands { cmd := executeCommand(config.Options, cmdName, params...)
if strings.HasPrefix(cmd, "monitor ") { cmd.Stdin = os.Stdin
params = append(params, "-o", "process plugin packet "+cmd) cmd.Stdout = os.Stdout
} else if cmd == "load" { cmd.Stderr = os.Stderr
params = append(params, "-o", "target modules load --load --slide 0") err = cmd.Run()
} else { if err != nil {
return fmt.Errorf("don't know how to convert GDB command %#v to LLDB", cmd) return &commandError{"failed to run " + cmdName + " with", result.Executable, err}
}
} }
} return nil
cmd := executeCommand(config.Options, cmdName, params...) })
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return &commandError{"failed to run " + cmdName + " with", result.Executable, err}
}
return nil
} }
// Run compiles and runs the given program. Depending on the target provided in // Run compiles and runs the given program. Depending on the target provided in
@@ -752,17 +707,16 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
return err return err
} }
_, err = buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run() return cmd.Run()
}) })
return err
} }
// buildAndRun builds and runs the given program, writing output to stdout and // buildAndRun builds and runs the given program, writing output to stdout and
// errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and // errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and
// passes command line arguments and evironment variables in a way appropriate // passes command line arguments and evironment variables in a way appropriate
// for the given emulator. // for the given emulator.
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) (builder.BuildResult, error) { func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) error {
// Determine whether we're on a system that supports environment variables // Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal, // and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment, // WebAssembly in the browser). If we're on a system without an environment,
@@ -813,82 +767,69 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
env = environmentVars env = environmentVars
} }
// Create a temporary directory for intermediary files.
tmpdir, err := os.MkdirTemp("", "tinygo")
if err != nil {
return builder.BuildResult{}, err
}
if !config.Options.Work {
defer os.RemoveAll(tmpdir)
}
// Build the binary to be run.
format, fileExt := config.EmulatorFormat() format, fileExt := config.EmulatorFormat()
result, err := builder.Build(pkgName, fileExt, tmpdir, config) return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
if err != nil { // If needed, set a timeout on the command. This is done in tests so
return result, err // they don't waste resources on a stalled test.
} var ctx context.Context
if timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(context.Background(), timeout)
defer cancel()
}
// If needed, set a timeout on the command. This is done in tests so // Set up the command.
// they don't waste resources on a stalled test. var name string
var ctx context.Context if config.Target.Emulator == "" {
if timeout != 0 { name = result.Binary
var cancel context.CancelFunc } else {
ctx, cancel = context.WithTimeout(context.Background(), timeout) emulator, err := config.Emulator(format, result.Binary)
defer cancel() if err != nil {
} return err
}
name = emulator[0]
emuArgs := append([]string(nil), emulator[1:]...)
args = append(emuArgs, args...)
}
var cmd *exec.Cmd
if ctx != nil {
cmd = exec.CommandContext(ctx, name, args...)
} else {
cmd = exec.Command(name, args...)
}
cmd.Env = env
// Set up the command. // Configure stdout/stderr. The stdout may go to a buffer, not a real
var name string // stdout.
if config.Target.Emulator == "" { cmd.Stdout = stdout
name = result.Binary cmd.Stderr = os.Stderr
} else { if config.EmulatorName() == "simavr" {
emulator, err := config.Emulator(format, result.Binary) cmd.Stdout = nil // don't print initial load commands
cmd.Stderr = stdout
}
// If this is a test, reserve CPU time for it so that increased
// parallelism doesn't blow up memory usage. If this isn't a test but
// simply `tinygo run`, then it is practically a no-op.
config.Options.Semaphore <- struct{}{}
defer func() {
<-config.Options.Semaphore
}()
// Run binary.
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(cmd.Path, cmd.Args...)
}
err := run(cmd, result)
if err != nil { if err != nil {
return result, err if ctx != nil && ctx.Err() == context.DeadlineExceeded {
stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
err = ctx.Err()
}
return &commandError{"failed to run compiled binary", result.Binary, err}
} }
name = emulator[0] return nil
emuArgs := append([]string(nil), emulator[1:]...) })
args = append(emuArgs, args...)
}
var cmd *exec.Cmd
if ctx != nil {
cmd = exec.CommandContext(ctx, name, args...)
} else {
cmd = exec.Command(name, args...)
}
cmd.Env = env
// Configure stdout/stderr. The stdout may go to a buffer, not a real
// stdout.
cmd.Stdout = stdout
cmd.Stderr = os.Stderr
if config.EmulatorName() == "simavr" {
cmd.Stdout = nil // don't print initial load commands
cmd.Stderr = stdout
}
// If this is a test, reserve CPU time for it so that increased
// parallelism doesn't blow up memory usage. If this isn't a test but
// simply `tinygo run`, then it is practically a no-op.
config.Options.Semaphore <- struct{}{}
defer func() {
<-config.Options.Semaphore
}()
// Run binary.
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(cmd.Path, cmd.Args...)
}
err = run(cmd, result)
if err != nil {
if ctx != nil && ctx.Err() == context.DeadlineExceeded {
stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
err = ctx.Err()
}
return result, &commandError{"failed to run compiled binary", result.Binary, err}
}
return result, nil
} }
func touchSerialPortAt1200bps(port string) (err error) { func touchSerialPortAt1200bps(port string) (err error) {
@@ -916,6 +857,8 @@ func touchSerialPortAt1200bps(port string) (err error) {
return fmt.Errorf("opening port: %s", err) return fmt.Errorf("opening port: %s", err)
} }
const maxMSDRetries = 10
func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) error { func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) error {
// find standard UF2 info path // find standard UF2 info path
var infoPath string var infoPath string
@@ -937,7 +880,7 @@ func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) erro
infoPath = path + "/INFO_UF2.TXT" infoPath = path + "/INFO_UF2.TXT"
} }
d, err := locateDevice(volume, infoPath, options.Timeout) d, err := locateDevice(volume, infoPath)
if err != nil { if err != nil {
return err return err
} }
@@ -966,7 +909,7 @@ func flashHexUsingMSD(volume, tmppath string, options *compileopts.Options) erro
destPath = path + "/" destPath = path + "/"
} }
d, err := locateDevice(volume, destPath, options.Timeout) d, err := locateDevice(volume, destPath)
if err != nil { if err != nil {
return err return err
} }
@@ -974,10 +917,10 @@ func flashHexUsingMSD(volume, tmppath string, options *compileopts.Options) erro
return moveFile(tmppath, d+"/flash.hex") return moveFile(tmppath, d+"/flash.hex")
} }
func locateDevice(volume, path string, timeout time.Duration) (string, error) { func locateDevice(volume, path string) (string, error) {
var d []string var d []string
var err error var err error
for start := time.Now(); time.Since(start) < timeout; { for i := 0; i < maxMSDRetries; i++ {
d, err = filepath.Glob(path) d, err = filepath.Glob(path)
if err != nil { if err != nil {
return "", err return "", err
@@ -1037,14 +980,18 @@ func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err e
var preferredPortIDs [][2]uint16 var preferredPortIDs [][2]uint16
for _, s := range usbInterfaces { for _, s := range usbInterfaces {
parts := strings.Split(s, ":") parts := strings.Split(s, ":")
if len(parts) != 2 { if len(parts) != 3 || (parts[0] != "acm" && parts[0] == "usb") {
return "", fmt.Errorf("could not parse USB VID/PID pair %q", s) // acm and usb are the two types of serial ports recognized
// under Linux (ttyACM*, ttyUSB*). Other operating systems don't
// generally make this distinction. If this is not one of the
// given USB devices, don't try to parse the USB IDs.
continue
} }
vid, err := strconv.ParseUint(parts[0], 16, 16) vid, err := strconv.ParseUint(parts[1], 16, 16)
if err != nil { if err != nil {
return "", fmt.Errorf("could not parse USB vendor ID %q: %w", parts[1], err) return "", fmt.Errorf("could not parse USB vendor ID %q: %w", parts[1], err)
} }
pid, err := strconv.ParseUint(parts[1], 16, 16) pid, err := strconv.ParseUint(parts[2], 16, 16)
if err != nil { if err != nil {
return "", fmt.Errorf("could not parse USB product ID %q: %w", parts[1], err) return "", fmt.Errorf("could not parse USB product ID %q: %w", parts[1], err)
} }
@@ -1384,9 +1331,9 @@ func main() {
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)") ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug") ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)") port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
timeout := flag.Duration("timeout", 20*time.Second, "the length of time to retry locating the MSD volume to be used for flashing")
programmer := flag.String("programmer", "", "which hardware programmer to use") programmer := flag.String("programmer", "", "which hardware programmer to use")
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags") ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable") llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output") cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
monitor := flag.Bool("monitor", false, "enable serial monitor") monitor := flag.Bool("monitor", false, "enable serial monitor")
@@ -1475,13 +1422,13 @@ func main() {
PrintAllocs: printAllocs, PrintAllocs: printAllocs,
Tags: []string(tags), Tags: []string(tags),
GlobalValues: globalVarValues, GlobalValues: globalVarValues,
WasmAbi: *wasmAbi,
Programmer: *programmer, Programmer: *programmer,
OpenOCDCommands: ocdCommands, OpenOCDCommands: ocdCommands,
LLVMFeatures: *llvmFeatures, LLVMFeatures: *llvmFeatures,
PrintJSON: flagJSON, PrintJSON: flagJSON,
Monitor: *monitor, Monitor: *monitor,
BaudRate: *baudrate, BaudRate: *baudrate,
Timeout: *timeout,
} }
if *printCommands { if *printCommands {
options.PrintCommands = printCommand options.PrintCommands = printCommand
@@ -1669,6 +1616,7 @@ func main() {
wg.Wait() wg.Wait()
close(fail) close(fail)
if _, fail := <-fail; fail { if _, fail := <-fail; fail {
fmt.Println("FAIL")
os.Exit(1) os.Exit(1)
} }
case "monitor": case "monitor":
+9 -2
View File
@@ -134,7 +134,10 @@ func TestBuild(t *testing.T) {
}) })
t.Run("AVR", func(t *testing.T) { t.Run("AVR", func(t *testing.T) {
t.Parallel() // LLVM backend crash:
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
// This bug is non-deterministic.
t.Skip("skipped due to non-deterministic backend bugs")
runPlatTests(optionsFromTarget("simavr", sema), tests, t) runPlatTests(optionsFromTarget("simavr", sema), tests, t)
}) })
@@ -191,6 +194,10 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
// Breaks interp. // Breaks interp.
continue continue
case "channel.go":
// Freezes after recv from closed channel.
continue
case "math.go": case "math.go":
// Stuck somewhere, not sure what's happening. // Stuck somewhere, not sure what's happening.
continue continue
@@ -329,7 +336,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
// Build the test binary. // Build the test binary.
stdout := &bytes.Buffer{} stdout := &bytes.Buffer{}
_, err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error { err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run() return cmd.Run()
}) })
if err != nil { if err != nil {
+1
View File
@@ -1,4 +1,5 @@
//go:build darwin || tinygo.wasm //go:build darwin || tinygo.wasm
// +build darwin tinygo.wasm
// This implementation of crypto/rand uses the arc4random_buf function // This implementation of crypto/rand uses the arc4random_buf function
// (available on both MacOS and WASI) to generate random numbers. // (available on both MacOS and WASI) to generate random numbers.
+1
View File
@@ -1,4 +1,5 @@
//go:build nrf || stm32 || (sam && atsamd51) || (sam && atsame5x) //go:build nrf || stm32 || (sam && atsamd51) || (sam && atsame5x)
// +build nrf stm32 sam,atsamd51 sam,atsame5x
package rand package rand
+1
View File
@@ -1,4 +1,5 @@
//go:build linux && !baremetal && !wasi //go:build linux && !baremetal && !wasi
// +build linux,!baremetal,!wasi
// This implementation of crypto/rand uses the /dev/urandom pseudo-file to // This implementation of crypto/rand uses the /dev/urandom pseudo-file to
// generate random numbers. // generate random numbers.
+1
View File
@@ -2,6 +2,7 @@
// Cortex-M System Control Block-related definitions. // Cortex-M System Control Block-related definitions.
//go:build cortexm //go:build cortexm
// +build cortexm
package arm package arm
+1
View File
@@ -1,4 +1,5 @@
//go:build avr && attiny85 //go:build avr && attiny85
// +build avr,attiny85
package avr package avr
+1
View File
@@ -3,6 +3,7 @@
// auto-generated by gen-device-svd.go) among devices in package nxp. // auto-generated by gen-device-svd.go) among devices in package nxp.
//go:build nxp && !mimxrt1062 //go:build nxp && !mimxrt1062
// +build nxp,!mimxrt1062
package nxp package nxp
+1
View File
@@ -3,6 +3,7 @@
// peripherals of the NXP MIMXRT1062. // peripherals of the NXP MIMXRT1062.
//go:build nxp && mimxrt1062 //go:build nxp && mimxrt1062
// +build nxp,mimxrt1062
package nxp package nxp
+1
View File
@@ -3,6 +3,7 @@
// auto-generated by gen-device-svd.go) among devices in package nxp. // auto-generated by gen-device-svd.go) among devices in package nxp.
//go:build nxp && mimxrt1062 //go:build nxp && mimxrt1062
// +build nxp,mimxrt1062
package nxp package nxp
+1
View File
@@ -3,6 +3,7 @@
// of the NXP MIMXRT1062. // of the NXP MIMXRT1062.
//go:build nxp && mimxrt1062 //go:build nxp && mimxrt1062
// +build nxp,mimxrt1062
package nxp package nxp
+1
View File
@@ -2,6 +2,7 @@
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go // atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
//go:build sam && atsamd51 //go:build sam && atsamd51
// +build sam,atsamd51
// These are the supported pchctrl function numberings on the atsamd51x // These are the supported pchctrl function numberings on the atsamd51x
// See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf // See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf

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