Compare commits

..

24 Commits

Author SHA1 Message Date
sago35 27a9e5d764 fix handling of DTGLIN 2022-05-30 18:18:35 +09:00
sago35 297d3cbcc2 Revert "shrink configuration descriptor"
This reverts commit b86574be9dcf261c0370cf33288e24e2382dac79.
2022-05-30 18:18:35 +09:00
sago35 2651b1d8bd fix packetComplete() 2022-05-30 18:18:35 +09:00
sago35 319b49e81c change to duplicate slice 2022-05-30 18:18:35 +09:00
sago35 7e493642eb shrink configuration descriptor 2022-05-30 18:18:35 +09:00
sago35 674b3b966f Add serial initialization 2022-05-30 18:18:35 +09:00
sago35 d9d29d3c68 add Interface Association Descriptor 2022-05-30 18:18:35 +09:00
sago35 e94ab78926 refactor USB init functions to minimize API 2022-05-30 18:18:35 +09:00
sago35 8fa58fdc6d more refactoring, verified CDC on macOS 2022-05-30 18:18:35 +09:00
sago35 4b81985d2b refactor USB package with device class build tags
added basic HID keyboard support for SAMx51 (not fully-functional)
2022-05-30 18:18:35 +09:00
ardnew 3b892cbe41 functional USB CDC-ACM for SAMx51 2022-05-30 18:18:35 +09:00
ardnew 5f6489d3cf add basic CDC-ACM UART Rx capability 2022-05-30 18:18:35 +09:00
sago35 2f50d24fec functioning USB-CDC class initialization 2022-05-30 18:18:35 +09:00
ardnew 6e29c17a8b initial file structure for SAMx51 USB device support 2022-05-30 18:18:35 +09:00
ardnew 67e68e484f set default Teensy 4.0 USB configuration to USB serial 2022-05-30 18:18:35 +09:00
ardnew 4b8b4243ec cleanup and compatibility updates from STM32H7 USB implemenetation 2022-05-30 18:18:35 +09:00
ardnew d2a1835ffc implemented USB HID composite keyboard support 2022-05-30 18:18:35 +09:00
ardnew 34b931b6f3 begin isolating target-specific USB code 2022-05-30 18:18:35 +09:00
ardnew 07964818d9 prevent heap alloc in control complete 2022-05-30 18:18:34 +09:00
ardnew 079eace344 rename package machine/usb2 to machine/usb 2022-05-30 18:18:34 +09:00
ardnew 69e8257e7b USB CDC-ACM UART Rx/Tx functioning for baseline target (Teensy 4.0/4.1) 2022-05-30 18:18:34 +09:00
ardnew 8103b3c3c8 functioning ACM device registration with host 2022-05-30 18:18:34 +09:00
ardnew a041a7866d cleanup unused code for baseline 2022-05-30 18:18:34 +09:00
ardnew 6583ec1448 begin USB refactor with package machine/usb2 2022-05-30 18:18:34 +09:00
877 changed files with 19194 additions and 22530 deletions
+32 -29
View File
@@ -6,16 +6,28 @@ commands:
- run:
name: "Pull submodules"
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:
steps:
- restore_cache:
keys:
- llvm-source-14-v3
- llvm-source-14-v2
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-14-v3
key: llvm-source-14-v2
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
@@ -33,82 +45,73 @@ commands:
steps:
- restore_cache:
keys:
- binaryen-linux-v2
- binaryen-linux-v1
- run:
name: "Build Binaryen"
command: |
make binaryen
- save_cache:
key: binaryen-linux-v2
key: binaryen-linux-v1
paths:
- build/wasm-opt
test-linux:
parameters:
llvm:
type: string
fmt-check:
type: boolean
default: true
steps:
- checkout
- submodules
- run:
name: "Install apt dependencies"
command: |
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
apt-get update
apt-get install --no-install-recommends -y \
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install --no-install-recommends \
llvm-<<parameters.llvm>>-dev \
clang-<<parameters.llvm>> \
libclang-<<parameters.llvm>>-dev \
lld-<<parameters.llvm>> \
gcc-avr \
avr-libc \
cmake \
ninja-build
- hack-ninja-jobs
- build-binaryen-linux
- restore_cache:
keys:
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v3-{{ checksum "go.mod" }}
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v2-{{ checksum "go.mod" }}
- llvm-source-linux
- run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache:
keys:
- wasi-libc-sysroot-systemclang-v6
- wasi-libc-sysroot-systemclang-v5
- run: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-systemclang-v6
key: wasi-libc-sysroot-systemclang-v5
paths:
- lib/wasi-libc/sysroot
- when:
condition: <<parameters.fmt-check>>
steps:
- run:
# Do this before gen-device so that it doesn't check the
# formatting of generated files.
name: Check Go code formatting
command: make fmt-check
- run: make gen-device -j4
- run: make smoketest XTENSA=0
- save_cache:
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
- run: make fmt-check
jobs:
test-llvm14-go118:
test-llvm13-go116:
docker:
- image: golang:1.18-buster
- image: circleci/golang:1.16-buster
steps:
- test-linux:
llvm: "14"
resource_class: large
llvm: "13"
workflows:
test-all:
jobs:
# This tests our lowest supported versions of Go and LLVM, to make sure at
# least the smoke tests still pass.
- test-llvm14-go118
- test-llvm13-go116
-2
View File
@@ -1,5 +1,3 @@
build/
llvm-*/
.github
.circleci
+22 -56
View File
@@ -7,33 +7,35 @@ on:
- dev
- release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-macos:
name: build-macos
runs-on: macos-11
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
- name: Install Dependencies
shell: bash
run: |
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
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
submodules: true
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-15-macos-v3
key: llvm-source-14-macos-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -43,22 +45,11 @@ jobs:
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-15-macos-v4
key: llvm-build-14-macos-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -72,24 +63,18 @@ jobs:
# build!
make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache wasi-libc sysroot
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-v4
key: wasi-libc-sysroot-v3
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball
run: make release -j3
- name: Test stdlib packages
@@ -110,23 +95,4 @@ jobs:
path: build/tinygo.darwin-amd64.tar.gz
- name: Smoke tests
shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo
test-macos-homebrew:
name: homebrew-install
runs-on: macos-latest
steps:
- name: Install LLVM
shell: bash
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
cache: true
- name: Build TinyGo
run: go install
- name: Check binary
run: tinygo version
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0
+31 -48
View File
@@ -7,10 +7,6 @@ on:
push:
branches: [ dev, fix-docker-llvm-build ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
push_to_registry:
name: build-push-dev
@@ -20,14 +16,14 @@ jobs:
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v1
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v3
with:
images: |
tinygo/tinygo-dev
@@ -36,65 +32,52 @@ jobs:
type=sha,format=long
type=raw,value=latest
- name: Log in to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Log in to Github Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-15
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Trigger Drivers repo build on Github Actions
- name: Trigger Drivers repo build on CircleCI
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger Bluetooth repo build on Github Actions
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/drivers/pipeline' \
--header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
- name: Trigger Bluetooth repo build on CircleCI
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyFS repo build on Github Actions
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/bluetooth/pipeline' \
--header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
- name: Trigger TinyFS repo build on CircleCI
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyFont repo build on Github Actions
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfs/pipeline' \
--header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
- name: Trigger TinyFont repo build on CircleCI
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyDraw repo build on Github Actions
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfont/pipeline' \
--header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
- name: Trigger TinyDraw repo build on CircleCI
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinydraw/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyTerm repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyterm/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinydraw/pipeline' \
--header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
+114 -167
View File
@@ -7,43 +7,38 @@ on:
- dev
- release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-linux:
# Build Linux binaries, ready for release.
# This runs inside an Alpine Linux container so we can more easily create a
# statically linked binary.
runs-on: ubuntu-latest
container:
image: golang:1.20-alpine
# This intentionally uses an older Linux image, so that we compile against
# an older glibc version and therefore are compatible with a wide range of
# Linux distributions.
runs-on: ubuntu-18.04
steps:
- name: Install apk dependencies
# tar: needed for actions/cache@v3
# git+openssh: needed for checkout (I think?)
# ruby: needed to install fpm
run: apk add tar git openssh make g++ ruby
- name: Work around CVE-2022-24765
# We're not on a multi-user machine, so this is safe.
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
submodules: true
- name: Cache Go
uses: actions/cache@v3
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
ninja-build
- name: Install Go
uses: actions/setup-go@v2
with:
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
go-version: '1.18.1'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-v1-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-15-linux-alpine-v3
key: llvm-source-14-linux-v2
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -53,22 +48,11 @@ jobs:
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-15-linux-alpine-v4
key: llvm-build-14-linux-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -76,50 +60,38 @@ jobs:
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies
apk add cmake samurai python3
# build!
make llvm-build
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-alpine-v1
key: binaryen-linux-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
apk add cmake samurai python3
make binaryen STATIC=1
run: make binaryen
- name: Cache wasi-libc
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-alpine-v1
key: wasi-libc-sysroot-linux-asserts-v4
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Install fpm
run: |
gem install --version 4.0.7 public_suffix
gem install --version 2.7.6 dotenv
gem install --no-document fpm
sudo gem install --no-document fpm
- name: Build TinyGo release
run: |
make release deb -j3 STATIC=1
make release deb -j3
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: linux-amd64-double-zipped
path: |
@@ -131,20 +103,17 @@ jobs:
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v2
with:
go-version: '1.20'
cache: true
go-version: '1.18.1'
- name: Install wasmtime
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
curl https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
curl https://wasmtime.dev/install.sh -sSf | bash
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Download release artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
- name: Extract release tarball
@@ -152,6 +121,17 @@ jobs:
mkdir -p ~/lib
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
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 smoketest
assert-test-linux:
@@ -160,7 +140,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
submodules: true
- name: Install apt dependencies
@@ -172,28 +152,34 @@ jobs:
qemu-system-arm \
qemu-system-riscv32 \
qemu-user \
gcc-avr \
avr-libc \
simavr \
ninja-build
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v2
with:
go-version: '1.20'
cache: true
go-version: '1.18.1'
- name: Install Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v2
with:
node-version: '14'
node-version: '12'
- name: Install wasmtime
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
curl https://wasmtime.dev/install.sh -sSf | bash
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-asserts-v1-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-15-linux-asserts-v3
key: llvm-source-14-linux-asserts-v2
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -203,22 +189,11 @@ jobs:
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-15-linux-asserts-v4
key: llvm-build-14-linux-asserts-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -230,14 +205,8 @@ jobs:
make llvm-build ASSERT=1
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-asserts-v1
@@ -246,10 +215,10 @@ jobs:
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen
- name: Cache wasi-libc
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-asserts-v5
key: wasi-libc-sysroot-linux-asserts-v4
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
@@ -263,6 +232,12 @@ jobs:
echo "$(pwd)/build" >> $GITHUB_PATH
- name: Test stdlib packages
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 wasmtest
- run: make tinygo-baremetal
@@ -279,24 +254,29 @@ jobs:
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Install apt dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
qemu-user \
g++-arm-linux-gnueabihf \
libc6-dev-armhf-cross
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v2
with:
go-version: '1.20'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
go-version: '1.18.1'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-arm-v2-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-15-linux-v3
key: llvm-source-14-linux-v2
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -306,22 +286,11 @@ jobs:
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-15-linux-arm-v4
key: llvm-build-14-linux-arm-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -335,14 +304,8 @@ jobs:
make llvm-build CROSS=arm-linux-gnueabihf
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-arm-v1
@@ -355,14 +318,12 @@ jobs:
make CROSS=arm-linux-gnueabihf binaryen
- name: Install fpm
run: |
sudo gem install --version 4.0.7 public_suffix
sudo gem install --version 2.7.6 dotenv
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=arm-linux-gnueabihf
- name: Download amd64 release
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
@@ -379,7 +340,7 @@ jobs:
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
cp -p build/release.deb /tmp/tinygo_armhf.deb
- name: Publish release artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: linux-arm-double-zipped
path: |
@@ -395,25 +356,30 @@ jobs:
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Install apt dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
qemu-user \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
ninja-build
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v2
with:
go-version: '1.20'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
go-version: '1.18.1'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-15-linux-v3
key: llvm-source-14-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -423,22 +389,11 @@ jobs:
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-15-linux-arm64-v4
key: llvm-build-14-linux-arm64-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -450,14 +405,8 @@ jobs:
make llvm-build CROSS=aarch64-linux-gnu
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-arm64-v1
@@ -469,14 +418,12 @@ jobs:
make CROSS=aarch64-linux-gnu binaryen
- name: Install fpm
run: |
sudo gem install --version 4.0.7 public_suffix
sudo gem install --version 2.7.6 dotenv
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=aarch64-linux-gnu
- name: Download amd64 release
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
@@ -493,7 +440,7 @@ jobs:
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
cp -p build/release.deb /tmp/tinygo_arm64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: linux-arm64-double-zipped
path: |
+24 -141
View File
@@ -7,20 +7,14 @@ on:
- dev
- release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-windows:
runs-on: windows-2022
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
- name: Install Go
uses: actions/setup-go@v2
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
go-version: '1.18.1'
- uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
@@ -29,19 +23,21 @@ jobs:
run: |
scoop install ninja binaryen
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
submodules: true
- name: Install Go
uses: actions/setup-go@v3
- name: Cache Go
uses: actions/cache@v2
with:
go-version: '1.20'
cache: true
- name: Restore cached LLVM source
uses: actions/cache/restore@v3
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
path: |
~/AppData/Local/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-15-windows-v4
key: llvm-source-14-windows-v2
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -51,22 +47,11 @@ jobs:
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save cached LLVM source
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore cached LLVM build
uses: actions/cache/restore@v3
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-15-windows-v5
key: llvm-build-14-windows-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -76,20 +61,14 @@ jobs:
rm -rf llvm-project
make llvm-source
# build!
make llvm-build CCACHE=OFF
make llvm-build
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save cached LLVM build
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache wasi-libc sysroot
uses: actions/cache@v3
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-v4
key: wasi-libc-sysroot-v3
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
@@ -99,9 +78,8 @@ jobs:
scoop install wasmtime
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball
shell: bash
run: make build/release -j4
- name: Make release artifact
shell: bash
@@ -114,109 +92,14 @@ jobs:
# - have a dobule-zipped artifact when downloaded from the UI
# - have a very slow artifact upload
# We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: release-double-zipped
path: build/release/release.zip
smoke-test-windows:
runs-on: windows-2022
needs: build-windows
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- 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
shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
stdlib-test-windows:
runs-on: windows-2022
needs: build-windows
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- 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
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
- name: Test stdlib packages
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
stdlib-wasi-test-windows:
runs-on: windows-2022
needs: build-windows
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- 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
run: make tinygo-test
- name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
run: make tinygo-test-wasi-fast
+1 -2
View File
@@ -23,7 +23,6 @@ llvm-project
build/*
# Ignore files generated by smoketest
test
test.bin
test.elf
test.exe
@@ -31,4 +30,4 @@ test.gba
test.hex
test.nro
test.wasm
wasm.wasm
wasm.wasm
+1 -1
View File
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.18+)
* Go (1.16+)
* Standard build tools (gcc/clang)
* git
* CMake
-288
View File
@@ -1,291 +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
---
* **command line**
- change to ignore PortReset failures
* **compiler**
- `compiler`: darwin/arm64 is aarch64, not arm
- `compiler`: don't clobber X18 and FP registers on darwin/arm64
- `compiler`: fix issue with methods on generic structs
- `compiler`: do not try to build generic functions
- `compiler`: fix type names for generic named structs
- `compiler`: fix multiple defined function issue for generic functions
- `compiler`: implement `unsafe.Alignof` and `unsafe.Sizeof` for generic code
* **standard library**
- `machine`: add DTR and RTS to Serialer interface
- `machine`: reorder pin definitions to improve pin list on tinygo.org
- `machine/usb`: add support for MIDI
- `machine/usb`: adjust buffer alignment (samd21, samd51, nrf52840)
- `machine/usb/midi`: add `NoteOn`, `NoteOff`, and `SendCC` methods
- `machine/usb/midi`: add definition of MIDI note number
- `runtime`: add benchmarks for memhash
- `runtime`: add support for printing slices via print/println
* **targets**
- `avr`: fix some apparent mistake in atmega1280/atmega2560 pin constants
- `esp32`: provide hardware pin constants
- `esp32`: fix WDT reset on the MCH2022 badge
- `esp32`: optimize SPI transmit
- `esp32c3`: provide hardware pin constants
- `esp8266`: provide hardware pin constants like `GPIO2`
- `nrf51`: define and use `P0_xx` constants
- `nrf52840`, `samd21`, `samd51`: unify bootloader entry process
- `nrf52840`, `samd21`, `samd51`: change usbSetup and sendZlp to public
- `nrf52840`, `samd21`, `samd51`: refactor handleStandardSetup and initEndpoint
- `nrf52840`, `samd21`, `samd51`: improve usb-device initialization
- `nrf52840`, `samd21`, `samd51`: move usbcdc to machine/usb/cdc
- `rp2040`: add usb serial vendor/product ID
- `rp2040`: add support for usb
- `rp2040`: change default for serial to usb
- `rp2040`: add support for `machine.EnterBootloader`
- `rp2040`: turn off pullup/down when input type is not specified
- `rp2040`: make picoprobe default openocd interface
- `samd51`: add support for `DAC1`
- `samd51`: improve TRNG
- `wasm`: stub `runtime.buffered`, `runtime.getchar`
- `wasi`: make leveldb runtime hash the default
* **boards**
- add Challenger RP2040 LoRa
- add MCH2022 badge
- add XIAO RP2040
- `clue`: remove pins `D21`..`D28`
- `feather-rp2040`, `macropad-rp2040`: fix qspi-flash settings
- `xiao-ble`: add support for flash-1200-bps-reset
- `gopherbot`, `gopherbot2`: add these aliases to simplify for newer users
0.24.0
---
* **command line**
- remove support for go 1.15
- remove support for LLVM 11 and LLVM 12
- add initial Go 1.19 beta support
- `test`: fix package/... syntax
* **compiler**
- add support for the embed package
- `builder`: improve error message for "command not found"
- `builder`: add support for ThinLTO on MacOS and Windows
- `builder`: free LLVM objects after use, to reduce memory leaking
- `builder`: improve `-no-debug` error messages
- `cgo`: be more strict: CGo now requires every Go file to import the headers it needs
- `compiler`: alignof(func) is 1 pointer, not 2
- `compiler`: add support for type parameters (aka generics)
- `compiler`: implement `recover()` built-in function
- `compiler`: support atomic, volatile, and LLVM memcpy-like functions in defer
- `compiler`: drop support for macos syscalls via inline assembly
- `interp`: do not try to interpret past task.Pause()
- `interp`: fix some buggy localValue handling
- `interp`: do not unroll loops
- `transform`: fix MakeGCStackSlots that caused a possible GC bug on WebAssembly
* **standard library**
- `os`: enable os.Stdin for baremetal target
- `reflect`: add `Value.UnsafePointer` method
- `runtime`: scan GC globals conservatively on Windows, MacOS, Linux and Nintendo Switch
- `runtime`: add per-map hash seeds
- `runtime`: handle nil map write panics
- `runtime`: add stronger hash functions
- `syscall`: implement `Getpagesize`
* **targets**
- `atmega2560`: support UART1-3 + example for uart
- `avr`: use compiler-rt for improved float64 support
- `avr`: simplify timer-based time
- `avr`: fix race condition in stack write
- `darwin`: add support for `GOARCH=arm64` (aka Apple Silicon)
- `darwin`: support `-size=short` and `-size=full` flag
- `rp2040`: replace sleep 'busy loop' with timer alarm
- `rp2040`: align api for `PortMaskSet`, `PortMaskClear`
- `rp2040`: fix GPIO interrupts
- `samd21`, `samd51`, `nrf52840`: add support for USBHID (keyboard / mouse)
- `wasm`: update wasi-libc version
- `wasm`: use newer WebAssembly features
* **boards**
- add Badger 2040
- `matrixportal-m4`: attach USB DP to the correct pin
- `teensy40`: add I2C support
- `wioterminal`: fix I2C definition
0.23.0
---
+13 -3
View File
@@ -1,8 +1,8 @@
# tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.20 AS tinygo-llvm
FROM golang:1.18 AS tinygo-llvm
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
@@ -15,8 +15,18 @@ FROM tinygo-llvm AS tinygo-llvm-build
RUN cd /tinygo/ && \
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
FROM tinygo-llvm-build AS tinygo-compiler
FROM tinygo-xtensa AS tinygo-compiler
COPY . /tinygo
+31 -140
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools.
# 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)))
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)
@@ -30,7 +30,7 @@ GO ?= go
export GOROOT = $(shell $(GO) env GOROOT)
# Flags to pass to go test.
GOTESTFLAGS ?=
GOTESTFLAGS ?= -v
# md5sum binary
MD5SUM = md5sum
@@ -38,16 +38,10 @@ MD5SUM = md5sum
# tinygo binary for tests
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
# Check for ccache if the user hasn't set it to on or off.
ifeq (, $(CCACHE))
# Use CCACHE for LLVM if possible
ifneq (, $(shell command -v ccache 2> /dev/null))
CCACHE := ON
else
CCACHE := OFF
endif
# Use CCACHE for LLVM if possible
ifneq (, $(shell command -v ccache 2> /dev/null))
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=ON'
endif
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
# Allow enabling LLVM assertions
ifeq (1, $(ASSERT))
@@ -56,29 +50,6 @@ else
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
endif
# Enable AddressSanitizer
ifeq (1, $(ASAN))
LLVM_OPTION += -DLLVM_USE_SANITIZER=Address
CGO_LDFLAGS += -fsanitize=address
endif
ifeq (1, $(STATIC))
# 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
# on most major Linux distributions. However, it is supported in Alpine
# Linux with musl.
CGO_LDFLAGS += -static
# Also set the thread stack size to 1MB. This is necessary on musl as the
# default stack size is 128kB and LLVM uses more than that.
# For more information, see:
# https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size
CGO_LDFLAGS += -Wl,-z,stack-size=1048576
# Build wasm-opt with static linking.
# For details, see:
# https://github.com/WebAssembly/binaryen/blob/version_102/.github/workflows/ci.yml#L181
BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static"
endif
# Cross compiling support.
ifneq ($(CROSS),)
CC = $(CROSS)-gcc
@@ -113,7 +84,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
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)
EXE = .exe
@@ -148,7 +119,7 @@ else
endif
# 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++
# Libraries that should be linked in for the statically linked LLD.
@@ -179,7 +150,7 @@ endif
clean:
@rm -rf build
FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
fmt:
@gofmt -l -w $(FMT_PATHS)
fmt-check:
@@ -236,13 +207,13 @@ gen-device-rp: build/gen-device-svd
# Get LLVM sources.
$(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
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(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.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
@@ -263,15 +234,15 @@ endif
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
cd lib/wasi-libc && make -j4 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" MALLOC_IMPL=none WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
# Build the Go compiler.
tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
TEST_PACKAGES_SLOW = \
@@ -286,6 +257,8 @@ TEST_PACKAGES_FAST = \
container/list \
container/ring \
crypto/des \
crypto/elliptic/internal/fiat \
crypto/internal/subtle \
crypto/md5 \
crypto/rc4 \
crypto/sha1 \
@@ -296,7 +269,6 @@ TEST_PACKAGES_FAST = \
encoding \
encoding/ascii85 \
encoding/base32 \
encoding/base64 \
encoding/csv \
encoding/hex \
go/scanner \
@@ -322,55 +294,29 @@ TEST_PACKAGES_FAST = \
unicode \
unicode/utf16 \
unicode/utf8 \
$(nil)
# Assume this will go away before Go2, so only check minor version.
ifeq ($(filter $(shell $(GO) env GOVERSION | cut -f 2 -d.), 16 17 18), )
TEST_PACKAGES_FAST += crypto/internal/nistec/fiat
else
TEST_PACKAGES_FAST += crypto/elliptic/internal/fiat
endif
# archive/zip requires os.ReadAt, which is not yet supported on windows
# bytes requires mmap
# compress/flate appears to hang on wasi
# compress/lzw appears to hang on wasi
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
# image requires recover(), which is not yet supported on wasi
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
# mime/quotedprintable requires syscall.Faccessat
# strconv requires recover() which is not yet supported on wasi
# text/tabwriter requries recover(), which is not yet supported on wasi
# text/template/parse requires recover(), which is not yet supported on wasi
# io/fs requires os.ReadDir, which is not yet supported on windows or wasi
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
# compress/flate fails windows go 1.18, https://github.com/tinygo-org/tinygo/issues/2762
# compress/lzw fails windows go 1.18 wasi, https://github.com/tinygo-org/tinygo/issues/2762
# Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \
archive/zip \
bytes \
compress/flate \
compress/lzw \
crypto/hmac \
debug/dwarf \
debug/plan9obj \
image \
io/fs \
io/ioutil \
mime/quotedprintable \
strconv \
testing/fstest \
text/tabwriter \
text/template/parse
testing/fstest
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
TEST_PACKAGES_WINDOWS := \
compress/flate \
compress/lzw \
crypto/hmac \
strconv \
text/template/parse \
$(nil)
compress/lzw
# Report platforms on which each standard library package is known to pass tests
jointmp := $(shell echo /tmp/join.$$$$)
@@ -385,15 +331,12 @@ report-stdlib-tests-pass:
# Standard library packages that pass tests quickly on the current platform
ifeq ($(shell uname),Darwin)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
TEST_IOFS := true
endif
ifeq ($(shell uname),Linux)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
TEST_IOFS := true
endif
ifeq ($(OS),Windows_NT)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
TEST_IOFS := false
endif
# Test known-working standard library packages.
@@ -401,12 +344,6 @@ endif
.PHONY: tinygo-test
tinygo-test:
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
@# requires a large stack-size. Hence, io/fs is only run conditionally.
@# For more details, see the comments on issue #3143.
ifeq ($(TEST_IOFS),true)
$(TINYGO) test -stack-size=6MB io/fs
endif
tinygo-test-fast:
$(TINYGO) test $(TEST_PACKAGES_HOST)
tinygo-bench:
@@ -416,9 +353,9 @@ tinygo-bench-fast:
# Same thing, except for wasi rather than the current platform.
tinygo-test-wasi:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-test-wasi-fast:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST)
tinygo-bench-wasi:
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasi-fast:
@@ -440,9 +377,6 @@ tinygo-baremetal:
.PHONY: smoketest
smoketest:
$(TINYGO) version
$(TINYGO) targets > /dev/null
# regression test for #2892
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
# compile-only platform-independent examples
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
# regression test for #2563
@@ -462,8 +396,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
@@ -474,18 +406,12 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
@$(MD5SUM) test.hex
# test simulated boards on play.tinygo.org
ifneq ($(WASM), 0)
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@@ -494,14 +420,14 @@ ifneq ($(WASM), 0)
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=reelboard examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=microbit examples/microbit-blink
$(TINYGO) build -size short -o test.wasm -tags=pca10040 examples/blinky2
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=pca10056 examples/blinky2
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/serial
@$(MD5SUM) test.wasm
endif
# test all targets/boards
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
@@ -552,8 +478,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=matrixportal-m4 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
@@ -620,26 +544,8 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=qtpy-rp2040 examples/echo
@$(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
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=tufty2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
@$(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
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=trinkey-qt2040 examples/temp
@$(MD5SUM) test.hex
# test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex
@@ -647,13 +553,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
@$(MD5SUM) test.hex
# test usb
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
@$(MD5SUM) test.hex
ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
@$(MD5SUM) test.hex
@@ -688,12 +587,11 @@ ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
@$(MD5SUM) test.hex
endif
ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
@$(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
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
@@ -708,6 +606,7 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
@$(MD5SUM) test.hex
endif
ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
@$(MD5SUM) test.bin
@@ -717,19 +616,15 @@ ifneq ($(XTENSA), 0)
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target m5stack examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
@$(MD5SUM) test.bin
endif
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
@$(MD5SUM) test.hex
ifneq ($(WASM), 0)
@@ -749,7 +644,6 @@ endif
@$(MD5SUM) test.hex
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=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=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
ifneq ($(OS),Windows_NT)
@@ -801,10 +695,8 @@ endif
@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/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/mman build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
@@ -823,7 +715,6 @@ endif
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
+3 -15
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.
The following 94 microcontroller boards are currently supported:
The following 85 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
* [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 CAN](https://www.adafruit.com/product/4759)
* [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 M4](https://www.adafruit.com/product/3800)
* [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 Matrix Portal M4](https://www.adafruit.com/product/4745)
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
@@ -68,9 +66,7 @@ The following 94 microcontroller boards are currently supported:
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
* [Adafruit QT Py RP2040](https://www.adafruit.com/product/4900)
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
* [Adafruit Trinkey QT2040](https://adafruit.com/product/5056)
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
@@ -93,13 +89,11 @@ The following 94 microcontroller boards are currently supported:
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
* [iLabs Challenger RP2040 LoRa](https://ilabs.se/product/challenger-rp2040-lora/)
* [M5Stack](https://docs.m5stack.com/en/core/basic)
* [M5Stack Core2](https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit)
* [M5Stamp C3](https://docs.m5stack.com/en/core/stamp_c3)
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
* [MCH2022 badge](https://badge.team/docs/badges/mch2022/)
* [Microchip SAM E54 Xplained Pro](https://www.microchip.com/developmenttools/productdetails/atsame54-xpro)
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
* [Nintendo Switch](https://www.nintendo.com/switch/)
@@ -111,8 +105,6 @@ The following 94 microcontroller boards are currently supported:
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
* [Pimoroni Badger2040](https://shop.pimoroni.com/products/badger-2040)
* [Pimoroni Tufty2040](https://shop.pimoroni.com/products/tufty-2040)
* [PineTime DevKit](https://www.pine64.org/pinetime/)
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
@@ -122,12 +114,10 @@ The following 94 microcontroller boards are currently supported:
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
* [Seeed XIAO ESP32C3](https://www.seeedstudio.com/Seeed-XIAO-ESP32C3-p-5431.html)
* [Seeed XIAO RP2040](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html)
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1-rev-b)
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
@@ -138,10 +128,8 @@ The following 94 microcontroller boards are currently supported:
* [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 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/)
* [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!
+1 -1
View File
@@ -18,7 +18,7 @@ import (
// makeArchive creates an arcive for static linking from a list of object files
// given as a parameter. It is equivalent to the following command:
//
// ar -rcs <archivePath> <objs...>
// ar -rcs <archivePath> <objs...>
func makeArchive(arfile *os.File, objs []string) error {
// Open the archive file.
arwriter := ar.NewWriter(arfile)
+233 -245
View File
@@ -14,7 +14,7 @@ import (
"fmt"
"go/types"
"hash/crc32"
"io/fs"
"io/ioutil"
"math/bits"
"os"
"os/exec"
@@ -42,8 +42,8 @@ type BuildResult struct {
// information. Used for GDB for example.
Executable string
// A path to the output binary. It is stored in the tmpdir directory of the
// Build function, so if it should be kept it must be copied or moved away.
// A path to the output binary. It will be removed after Build returns, so
// 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
// .hex for example (instead of the usual ELF).
Binary string
@@ -94,16 +94,23 @@ type packageAction struct {
//
// The error value may be of type *MultiError. Callers will likely want to check
// 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.
// Used as a cache key for package builds.
compilerBuildID, err := ReadBuildID()
if err != nil {
return BuildResult{}, err
return err
}
// Create a temporary directory for intermediary files.
dir, err := ioutil.TempDir("", "tinygo")
if err != nil {
return err
}
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
@@ -112,7 +119,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if cacheDir == "off" {
// Use temporary build directory instead, effectively disabling the
// build cache.
cacheDir = tmpdir
cacheDir = dir
}
// Check for a libc dependency.
@@ -122,40 +129,40 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
var libcDependencies []*compileJob
switch config.Target.Libc {
case "darwin-libSystem":
job := makeDarwinLibSystemJob(config, tmpdir)
job := makeDarwinLibSystemJob(config, dir)
libcDependencies = append(libcDependencies, job)
case "musl":
job, unlock, err := Musl.load(config, tmpdir)
job, unlock, err := Musl.load(config, dir)
if err != nil {
return BuildResult{}, err
return err
}
defer unlock()
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
libcDependencies = append(libcDependencies, job)
case "picolibc":
libcJob, unlock, err := Picolibc.load(config, tmpdir)
libcJob, unlock, err := Picolibc.load(config, dir)
if err != nil {
return BuildResult{}, err
return err
}
defer unlock()
libcDependencies = append(libcDependencies, libcJob)
case "wasi-libc":
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
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`?")
if _, err := os.Stat(path); os.IsNotExist(err) {
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
}
libcDependencies = append(libcDependencies, dummyCompileJob(path))
case "mingw-w64":
_, unlock, err := MinGW.load(config, tmpdir)
_, unlock, err := MinGW.load(config, dir)
if err != nil {
return BuildResult{}, err
return err
}
unlock()
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...)
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(dir)...)
case "":
// no library specified, so nothing to do
default:
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
}
optLevel, sizeLevel, _ := config.OptLevels()
@@ -163,20 +170,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
Triple: config.Triple(),
CPU: config.CPU(),
Features: config.Features(),
ABI: config.ABI(),
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
SizeLevel: sizeLevel,
TinyGoVersion: goenv.Version,
Scheduler: config.Scheduler(),
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.StackSize(),
DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(),
Debug: true,
LTO: config.LTO() != "legacy",
}
// Load the target machine, which is the LLVM object that contains all
@@ -184,29 +188,20 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// address spaces, etc).
machine, err := compiler.NewTargetMachine(compilerConfig)
if err != nil {
return BuildResult{}, err
return err
}
defer machine.Dispose()
// Load entire program AST into memory.
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
result := BuildResult{
ModuleRoot: lprogram.MainPkg().Module.Dir,
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
if err != nil {
return err
}
err = lprogram.Parse()
if err != nil {
return result, err
return err
}
// Create the *ssa.Program. This does not yet build the entire SSA of the
@@ -275,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
},
}
@@ -289,7 +284,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
for _, imported := range pkg.Pkg.Imports() {
job, ok := packageActionIDJobs[imported.Path()]
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)
actionIDDependencies = append(actionIDDependencies, job)
@@ -307,6 +302,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
actionID := packageAction{
ImportPath: pkg.ImportPath,
CompilerBuildID: string(compilerBuildID),
TinyGoVersion: goenv.Version,
LLVMVersion: llvm.Version,
Config: compilerConfig,
CFlags: pkg.CFlags,
@@ -374,7 +370,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Packages are compiled independently anyway.
for _, cgoHeader := range pkg.CGoHeaders {
// Store the header text in a temporary file.
f, err := os.CreateTemp(tmpdir, "cgosnippet-*.c")
f, err := ioutil.TempFile(dir, "cgosnippet-*.c")
if err != nil {
return err
}
@@ -422,7 +418,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return errors.New("global not found: " + globalName)
}
name := global.Name()
newGlobal := llvm.AddGlobal(mod, global.GlobalValueType(), name+".tmp")
newGlobal := llvm.AddGlobal(mod, global.Type().ElementType(), name+".tmp")
global.ReplaceAllUsesWith(newGlobal)
global.EraseFromParentAsGlobal()
newGlobal.SetName(name)
@@ -435,7 +431,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path())
}
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
err := interp.RunFunc(pkgInit, config.DumpSSA())
if err != nil {
return err
}
@@ -443,34 +439,48 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return errors.New("verification error after interpreting " + pkgInit.Name())
}
transform.OptimizePackage(mod, config)
// Run function passes for each function in the module.
// These passes are intended to be run on each function right
// after they're created to reduce IR size (and maybe also for
// cache locality to improve performance), but for now they're
// run here for each function in turn. Maybe this can be
// improved in the future.
builder := llvm.NewPassManagerBuilder()
defer builder.Dispose()
builder.SetOptLevel(optLevel)
builder.SetSizeLevel(sizeLevel)
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
defer funcPasses.Dispose()
builder.PopulateFunc(funcPasses)
funcPasses.InitializeFunc()
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
if fn.IsDeclaration() {
continue
}
funcPasses.RunFunc(fn)
}
funcPasses.FinalizeFunc()
// Serialize the LLVM module as a bitcode file.
// Write to a temporary path that is renamed to the destination
// file to avoid race conditions with other TinyGo invocatiosn
// that might also be compiling this package at the same time.
f, err := os.CreateTemp(filepath.Dir(job.result), filepath.Base(job.result))
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
if err != nil {
return err
}
if compilerConfig.LTO {
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
if runtime.GOOS == "windows" {
// Work around a problem on Windows.
// For some reason, WriteBitcodeToFile causes TinyGo to
// exit with the following message:
// LLVM ERROR: IO failure on output stream: Bad file descriptor
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
defer buf.Dispose()
_, err = f.Write(buf.Bytes())
} else {
if runtime.GOOS == "windows" {
// Work around a problem on Windows.
// For some reason, WriteBitcodeToFile causes TinyGo to
// exit with the following message:
// LLVM ERROR: IO failure on output stream: Bad file descriptor
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
defer buf.Dispose()
_, err = f.Write(buf.Bytes())
} else {
// Otherwise, write bitcode directly to the file (probably
// faster).
err = llvm.WriteBitcodeToFile(mod, f)
}
// Otherwise, write bitcode directly to the file (probably
// faster).
err = llvm.WriteBitcodeToFile(mod, f)
}
if err != nil {
// WriteBitcodeToFile doesn't produce a useful error on its
@@ -518,11 +528,24 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Create runtime.initAll function that calls the runtime
// initializer of each package.
initAllModule := createInitAll(ctx, config, compilerConfig, lprogram.Sorted())
err := llvm.LinkModules(mod, initAllModule)
if err != nil {
return fmt.Errorf("failed to link module: %w", err)
llvmInitFn := mod.NamedFunction("runtime.initAll")
llvmInitFn.SetLinkage(llvm.InternalLinkage)
llvmInitFn.SetUnnamedAddr(true)
transform.AddStandardAttributes(llvmInitFn, config)
llvmInitFn.Param(0).SetName("context")
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
irbuilder := mod.Context().NewBuilder()
defer irbuilder.Dispose()
irbuilder.SetInsertPointAtEnd(block)
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
for _, pkg := range lprogram.Sorted() {
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path())
}
irbuilder.CreateCall(pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
}
irbuilder.CreateRetVoid()
// After linking, functions should (as far as possible) be set to
// private linkage or internal linkage. The compiler package marks
@@ -555,7 +578,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Run all optimization passes, which are much more effective now
// that the optimizer can see the whole program at once.
err = optimizeProgram(mod, config)
err := optimizeProgram(mod, config)
if err != nil {
return err
}
@@ -576,24 +599,29 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Run jobs to produce the LLVM module.
err := runJobs(programJob, config.Options.Semaphore)
if err != nil {
return result, err
return err
}
// Generate output.
switch outext {
case ".o":
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return result, err
return err
}
defer llvmBuf.Dispose()
return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc":
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
var buf llvm.MemoryBuffer
if config.UseThinLTO() {
buf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
} else {
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
}
defer buf.Dispose()
return result, os.WriteFile(outpath, buf.Bytes(), 0666)
return ioutil.WriteFile(outpath, buf.Bytes(), 0666)
case ".ll":
data := []byte(mod.String())
return result, os.WriteFile(outpath, data, 0666)
return ioutil.WriteFile(outpath, data, 0666)
default:
panic("unreachable")
}
@@ -604,63 +632,42 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// run all jobs in parallel as far as possible.
// Add job to write the output object file.
objfile := filepath.Join(tmpdir, "main.o")
objfile := filepath.Join(dir, "main.o")
outputObjectFileJob := &compileJob{
description: "generate output file",
dependencies: []*compileJob{programJob},
result: objfile,
run: func(*compileJob) error {
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
var llvmBuf llvm.MemoryBuffer
if config.UseThinLTO() {
llvmBuf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
} else {
var err error
llvmBuf, err = machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
}
defer llvmBuf.Dispose()
return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
},
}
// Add job to create the runtime.initAll function in a new module.
initAllJob := &compileJob{
description: "create runtime.initAll",
result: filepath.Join(tmpdir, "runtime-initAll.bc"),
run: func(job *compileJob) (err error) {
// Create module with runtime.initAll.
ctx := llvm.NewContext()
defer ctx.Dispose()
initAllMod := createInitAll(ctx, config, compilerConfig, lprogram.Sorted())
defer initAllMod.Dispose()
// Write module to bitcode file.
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(initAllMod)
defer llvmBuf.Dispose()
return os.WriteFile(job.result, llvmBuf.Bytes(), 0666)
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
},
}
// Prepare link command.
var linkerDependencies []*compileJob
switch config.LTO() {
case "legacy":
// Link all Go bitcode files together into a single large module and
// then do a ThinLTO link with the resulting large module + extra C
// files (from CGo etc).
linkerDependencies = append(linkerDependencies, outputObjectFileJob)
case "thin":
// Do a real thin link, with each Go package in a separate translation
// unit. This is faster than merging them into one big LTO module.
linkerDependencies = append(linkerDependencies, packageJobs...)
linkerDependencies = append(linkerDependencies, initAllJob)
}
result.Executable = filepath.Join(tmpdir, "main")
linkerDependencies := []*compileJob{outputObjectFileJob}
executable := filepath.Join(dir, "main")
if config.GOOS() == "windows" {
result.Executable += ".exe"
executable += ".exe"
}
result.Binary = result.Executable // final file
ldflags := append(config.LDFlags(), "-o", result.Executable)
tmppath := executable // final file
ldflags := append(config.LDFlags(), "-o", executable)
// Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache.
if config.Target.RTLib == "compiler-rt" {
job, unlock, err := CompilerRT.load(config, tmpdir)
job, unlock, err := CompilerRT.load(config, dir)
if err != nil {
return result, err
return err
}
defer unlock()
linkerDependencies = append(linkerDependencies, job)
@@ -674,7 +681,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{
description: "compile extra file " + path,
run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
job.result = result
return err
},
@@ -692,7 +699,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{
description: "compile CGo file " + abspath,
run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.Options.PrintCommands)
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
job.result = result
return err
},
@@ -713,18 +720,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Add embedded files.
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.
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.Target.Linker == "wasm-ld" {
// Don't just strip debug information, also compress relocations
// while we're at it. Relocations can only be compressed when debug
@@ -734,8 +738,21 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// ld.lld is also used on Linux.
ldflags = append(ldflags, "--strip-debug")
} else {
// Other linkers may have different flags.
return result, errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
switch config.GOOS() {
case "linux":
// Either real linux or an embedded system (like AVR) that
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
// case (not ld.lld - that case is handled above).
ldflags = append(ldflags, "-Wl,--strip-debug")
case "darwin":
// MacOS (darwin) doesn't have a linker flag to strip debug
// information. Apple expects you to use the strip command
// instead.
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
default:
// Other OSes may have different flags.
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
}
}
}
@@ -751,41 +768,43 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
ldflags = append(ldflags, dependency.result)
}
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
if config.CodeModel() != "default" {
ldflags = append(ldflags,
"-mllvm", "-code-model="+config.CodeModel())
}
if sizeLevel >= 2 {
// Workaround with roughly the same effect as
// https://reviews.llvm.org/D119342.
// Can hopefully be removed in LLVM 15.
ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0")
}
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(config.Target.Linker, ldflags...)
}
if config.UseThinLTO() {
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
if config.CodeModel() != "default" {
ldflags = append(ldflags,
"-mllvm", "-code-model="+config.CodeModel())
}
if sizeLevel >= 2 {
// Workaround with roughly the same effect as
// https://reviews.llvm.org/D119342.
// Can hopefully be removed in LLVM 15.
ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0")
}
}
err = link(config.Target.Linker, ldflags...)
if err != nil {
return &commandError{"failed to link", result.Executable, err}
return &commandError{"failed to link", executable, err}
}
var calculatedStacks []string
@@ -794,7 +813,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Try to determine stack sizes at compile time.
// Don't do this by default as it usually doesn't work on
// unsupported architectures.
calculatedStacks, stackSizes, err = determineStackSizes(mod, result.Executable)
calculatedStacks, stackSizes, err = determineStackSizes(mod, executable)
if err != nil {
return err
}
@@ -804,51 +823,41 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if config.AutomaticStackSize() {
// Modify the .tinygo_stacksizes section that contains a stack size
// for each goroutine.
err = modifyStackSizes(result.Executable, stackSizeLoads, stackSizes)
err = modifyStackSizes(executable, stackSizeLoads, stackSizes)
if err != nil {
return fmt.Errorf("could not modify stack sizes: %w", err)
}
}
if config.RP2040BootPatch() {
// Patch the second stage bootloader CRC into the .boot2 section
err = patchRP2040BootCRC(result.Executable)
err = patchRP2040BootCRC(executable)
if err != nil {
return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err)
}
}
// Run wasm-opt for wasm binaries
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
var opt string
// Run wasm-opt if necessary.
if config.Scheduler() == "asyncify" {
var optLevel, shrinkLevel int
switch config.Options.Opt {
case "none", "0":
opt = "-O0"
case "1":
opt = "-O1"
optLevel = 1
case "2":
opt = "-O2"
optLevel = 2
case "s":
opt = "-Os"
optLevel = 2
shrinkLevel = 1
case "z":
opt = "-Oz"
optLevel = 2
shrinkLevel = 2
default:
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
}
var args []string
if config.Scheduler() == "asyncify" {
args = append(args, "--asyncify")
}
args = append(args,
opt,
"-g",
result.Executable,
"--output", result.Executable,
)
cmd := exec.Command(goenv.Get("WASMOPT"), args...)
cmd := exec.Command(goenv.Get("WASMOPT"), "--asyncify", "-g",
"--optimize-level", strconv.Itoa(optLevel),
"--shrink-level", strconv.Itoa(shrinkLevel),
executable, "--output", executable)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -864,7 +873,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
for _, pkg := range lprogram.Sorted() {
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
}
sizes, err := loadProgramSize(result.Executable, packagePathMap)
sizes, err := loadProgramSize(executable, packagePathMap)
if err != nil {
return err
}
@@ -900,7 +909,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// is simpler and cannot be parallelized.
err = runJobs(linkJob, config.Options.Semaphore)
if err != nil {
return result, err
return err
}
// Get an Intel .hex file or .bin file from the .elf file.
@@ -911,43 +920,56 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
case "hex", "bin":
// Extract raw binary, either encoding it as a hex file or as a raw
// firmware file.
result.Binary = filepath.Join(tmpdir, "main"+outext)
err := objcopy(result.Executable, result.Binary, outputBinaryFormat)
tmppath = filepath.Join(dir, "main"+outext)
err := objcopy(executable, tmppath, outputBinaryFormat)
if err != nil {
return result, err
return err
}
case "uf2":
// Get UF2 from the .elf file.
result.Binary = filepath.Join(tmpdir, "main"+outext)
err := convertELFFileToUF2File(result.Executable, result.Binary, config.Target.UF2FamilyID)
tmppath = filepath.Join(dir, "main"+outext)
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
if err != nil {
return result, err
return err
}
case "esp32", "esp32-img", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM
// bootloader).
result.Binary = filepath.Join(tmpdir, "main"+outext)
err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat)
tmppath = filepath.Join(dir, "main"+outext)
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
if err != nil {
return result, err
return err
}
case "nrf-dfu":
// special format for nrfutil for Nordic chips
tmphexpath := filepath.Join(tmpdir, "main.hex")
err := objcopy(result.Executable, tmphexpath, "hex")
tmphexpath := filepath.Join(dir, "main.hex")
err := objcopy(executable, tmphexpath, "hex")
if err != nil {
return result, err
return err
}
result.Binary = filepath.Join(tmpdir, "main"+outext)
err = makeDFUFirmwareImage(config.Options, tmphexpath, result.Binary)
tmppath = filepath.Join(dir, "main"+outext)
err = makeDFUFirmwareImage(config.Options, tmphexpath, tmppath)
if err != nil {
return result, err
return err
}
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
@@ -1056,50 +1078,11 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
return outfile.Name(), outfile.Close()
}
// Create a module with the runtime.initAll function that calls all package
// initializers in initialization order.
func createInitAll(ctx llvm.Context, config *compileopts.Config, compilerConfig *compiler.Config, pkgs []*loader.Package) llvm.Module {
// Create LLVM module.
mod := ctx.NewModule("runtime-initAll")
// Add datalayout string.
machine, err := compiler.NewTargetMachine(compilerConfig)
if err != nil {
panic(err) // extremely unlikely to fail here (NewTargetMachine is called many times before)
}
defer machine.Dispose()
targetData := machine.CreateTargetData()
defer targetData.Dispose()
mod.SetTarget(config.Triple())
mod.SetDataLayout(targetData.String())
// Create empty runtime.initAll function.
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{i8ptrType}, false)
fn := llvm.AddFunction(mod, "runtime.initAll", fnType)
fn.SetUnnamedAddr(true)
transform.AddStandardAttributes(fn, config)
// Add calls to package initializers.
fn.Param(0).SetName("context")
block := mod.Context().AddBasicBlock(fn, "entry")
irbuilder := mod.Context().NewBuilder()
defer irbuilder.Dispose()
irbuilder.SetInsertPointAtEnd(block)
for _, pkg := range pkgs {
pkgInit := llvm.AddFunction(mod, pkg.Pkg.Path()+".init", fnType)
irbuilder.CreateCall(fnType, pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
}
irbuilder.CreateRetVoid()
return mod
}
// optimizeProgram runs a series of optimizations and transformations that are
// needed to convert a program to its final form. Some transformations are not
// optional and must be run as the compiler expects them to run.
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
err := interp.Run(mod, config.DumpSSA())
if err != nil {
return err
}
@@ -1116,6 +1099,10 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
}
}
if config.GOOS() != "darwin" && !config.UseThinLTO() {
transform.ApplyFunctionSections(mod) // -ffunction-sections
}
// Insert values from -ldflags="-X ..." into the IR.
err = setGlobalValues(mod, config.Options.GlobalValues)
if err != nil {
@@ -1126,6 +1113,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
// cannot be represented exactly in JavaScript (JS only has doubles). To
// keep functions interoperable, pass int64 types as pointers to
// stack-allocated values.
// Use -wasm-abi=generic to disable this behaviour.
if config.WasmAbi() == "js" {
err := transform.ExternalInt64AsPtr(mod, config)
if err != nil {
@@ -1176,7 +1164,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
// initializer.
initializerType := global.GlobalValueType()
initializerType := global.Type().ElementType()
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
return fmt.Errorf("%s: not a string", globalName)
}
@@ -1195,7 +1183,7 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
// Create the string value, which is a {ptr, len} pair.
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] {
return fmt.Errorf("%s: not a string", globalName)
}
@@ -1406,10 +1394,10 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
//
// It might print something like the following:
//
// function stack usage (in bytes)
// Reset_Handler 316
// examples/blinky2.led1 92
// runtime.run$1 300
// function stack usage (in bytes)
// Reset_Handler 316
// examples/blinky2.led1 92
// runtime.run$1 300
func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) {
// Print the sizes of all stacks.
fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
+2 -2
View File
@@ -2,6 +2,7 @@ package builder
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -59,7 +60,6 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"},
} {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" {
@@ -90,7 +90,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
// Create a very simple C input file.
srcpath := filepath.Join(testDir, "test.c")
err = os.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
err = ioutil.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
if err != nil {
t.Fatalf("could not write target file %s: %s", srcpath, err)
}
+1 -1
View File
@@ -24,7 +24,7 @@ func ReadBuildID() ([]byte, error) {
defer f.Close()
switch runtime.GOOS {
case "linux", "freebsd", "android":
case "linux", "freebsd":
// Read the GNU build id section. (Not sure about FreeBSD though...)
file, err := elf.NewFile(f)
if err != nil {
+2 -14
View File
@@ -162,15 +162,6 @@ var aeabiBuiltins = []string{
"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.
// These symbols are for operations that cannot be emitted with a single
// instruction or a short sequence of instructions for that target.
@@ -190,14 +181,11 @@ var CompilerRT = Library{
// Development build.
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
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
builtins = append(builtins, aeabiBuiltins...)
}
if strings.HasPrefix(target, "avr") {
builtins = append(builtins, avrBuiltins...)
}
return builtins, nil
return builtins
},
}
+51 -44
View File
@@ -10,7 +10,7 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"sort"
@@ -33,30 +33,30 @@ import (
// Because of this complexity, every file has in fact two cached build outputs:
// the file itself, and the list of dependencies. Its operation is as follows:
//
// depfile = hash(path, compiler, cflags, ...)
// if depfile exists:
// outfile = hash of all files and depfile name
// if outfile exists:
// # cache hit
// return outfile
// # cache miss
// tmpfile = compile file
// read dependencies (side effect of compile)
// write depfile
// outfile = hash of all files and depfile name
// rename tmpfile to outfile
// depfile = hash(path, compiler, cflags, ...)
// if depfile exists:
// outfile = hash of all files and depfile name
// if outfile exists:
// # cache hit
// return outfile
// # cache miss
// tmpfile = compile file
// read dependencies (side effect of compile)
// write depfile
// outfile = hash of all files and depfile name
// rename tmpfile to outfile
//
// There are a few edge cases that are not handled:
// - If a file is added to an include path, that file may be included instead of
// some other file. This would be fixed by also including lookup failures in the
// dependencies file, but I'm not aware of a compiler which does that.
// - The Makefile syntax that compilers output has issues, see readDepFile for
// details.
// - A header file may be changed to add/remove an include. This invalidates the
// depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it
// could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
// - If a file is added to an include path, that file may be included instead of
// some other file. This would be fixed by also including lookup failures in the
// dependencies file, but I'm not aware of a compiler which does that.
// - The Makefile syntax that compilers output has issues, see readDepFile for
// details.
// - A header file may be changed to add/remove an include. This invalidates the
// depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it
// could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
// Hash input file.
fileHash, err := hashFile(abspath)
if err != nil {
@@ -67,6 +67,11 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
defer unlock()
ext := ".o"
if thinlto {
ext = ".bc"
}
// Create cache key for the dependencies file.
buf, err := json.Marshal(struct {
Path string
@@ -88,7 +93,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
// Load dependencies file, if possible.
depfileName := "dep-" + depfileNameHash + ".json"
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
depfileBuf, err := os.ReadFile(depfileCachePath)
depfileBuf, err := ioutil.ReadFile(depfileCachePath)
var dependencies []string // sorted list of dependency paths
if err == nil {
// There is a dependency file, that's great!
@@ -99,31 +104,34 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
}
// Obtain hashes of all the files listed as a dependency.
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
outpath, err := makeCFileCachePath(dependencies, depfileNameHash, ext)
if err == nil {
if _, err := os.Stat(outpath); err == nil {
return outpath, nil
} else if !errors.Is(err, fs.ErrNotExist) {
} else if !os.IsNotExist(err) {
return "", err
}
}
} else if !errors.Is(err, fs.ErrNotExist) {
} else if !os.IsNotExist(err) {
// expected either nil or IsNotExist
return "", err
}
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*.bc")
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*"+ext)
if err != nil {
return "", err
}
objTmpFile.Close()
depTmpFile, err := os.CreateTemp(tmpdir, "dep-*.d")
depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
if err != nil {
return "", err
}
depTmpFile.Close()
flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name(), "-flto=thin") // autogenerate dependencies
flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
if thinlto {
flags = append(flags, "-flto=thin")
}
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
// If this is an assembly file (.s or .S, lowercase or uppercase), then
@@ -158,7 +166,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
sort.Strings(dependencySlice)
// Write dependencies file.
f, err := os.CreateTemp(filepath.Dir(depfileCachePath), depfileName)
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
if err != nil {
return "", err
}
@@ -181,7 +189,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
}
// Move temporary object file to final location.
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext)
if err != nil {
return "", err
}
@@ -196,7 +204,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
// Create a cache path (a path in GOCACHE) to store the output of a compiler
// job. This path is based on the dep file name (which is a hash of metadata
// including compiler flags) and the hash of all input files in the paths slice.
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, error) {
// Hash all input files.
fileHashes := make(map[string]string, len(paths))
for _, path := range paths {
@@ -221,7 +229,7 @@ func makeCFileCachePath(paths []string, depfileNameHash string) (string, error)
outFileNameBuf := sha512.Sum512_224(buf)
cacheKey := hex.EncodeToString(outFileNameBuf[:])
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".bc")
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+ext)
return outpath, nil
}
@@ -244,14 +252,13 @@ func hashFile(path string) (string, error) {
// file is assumed to have a single target named deps.
//
// There are roughly three make syntax variants:
// - BSD make, which doesn't support any escaping. This means that many special
// characters are not supported in file names.
// - GNU make, which supports escaping using a backslash but when it fails to
// find a file it tries to fall back with the literal path name (to match BSD
// make).
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
// any weird characters.
//
// - BSD make, which doesn't support any escaping. This means that many special
// characters are not supported in file names.
// - GNU make, which supports escaping using a backslash but when it fails to
// find a file it tries to fall back with the literal path name (to match BSD
// make).
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
// any weird characters.
// Clang supports two variants: a format that's a compromise between BSD and GNU
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
// is at least somewhat sane. This last format isn't perfect either: it does not
@@ -259,7 +266,7 @@ func hashFile(path string) (string, error) {
// allowed on Windows, but of course can be used on POSIX like systems. Still,
// it's the most sane of any of the formats so readDepFile will use that format.
func readDepFile(filename string) ([]string, error) {
buf, err := os.ReadFile(filename)
buf, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
+4 -19
View File
@@ -1,4 +1,4 @@
//go:build byollvm
// +build byollvm
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
//
@@ -101,9 +101,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
// Target Options
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.Features = Args.getAllArgValues(OPT_target_feature);
@@ -206,14 +203,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
.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;
}
@@ -264,8 +253,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
assert(MRI && "Unable to create target register info!");
MCTargetOptions MCOptions;
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
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.
std::unique_ptr<MCObjectFileInfo> MOFI(
TheTarget->createMCObjectFileInfo(Ctx, PIC));
if (Opts.DarwinTargetVariantTriple)
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
Ctx.setObjectFileInfo(MOFI.get());
if (Opts.SaveTemporaryLabels)
@@ -362,7 +347,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
std::unique_ptr<MCCodeEmitter> CE;
if (Opts.ShowEncoding)
CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx));
CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
std::unique_ptr<MCAsmBackend> MAB(
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
@@ -382,7 +367,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
}
std::unique_ptr<MCCodeEmitter> CE(
TheTarget->createMCCodeEmitter(*MCII, Ctx));
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
std::unique_ptr<MCAsmBackend> MAB(
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
assert(MAB && "Unable to create asm backend!");
@@ -404,7 +389,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) {
MCSection *AsmLabel = Ctx.getMachOSection(
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
Str.get()->switchSection(AsmLabel);
Str.get()->SwitchSection(AsmLabel);
Str.get()->emitZeros(1);
}
-7
View File
@@ -85,9 +85,6 @@ struct AssemblerInvocation {
unsigned IncrementalLinkerCompatible : 1;
unsigned EmbedBitcode : 1;
/// Whether to emit DWARF unwind info.
EmitDwarfUnwindType EmitDwarfUnwind;
/// The name of the relocation model to use.
std::string RelocationModel;
@@ -95,9 +92,6 @@ struct AssemblerInvocation {
/// otherwise.
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:
@@ -118,7 +112,6 @@ public:
Dwarf64 = 0;
DwarfVersion = 0;
EmbedBitcode = 0;
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
}
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/CodeGen/CodeGenAction.h>
+2 -2
View File
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
}
if major != 1 || minor < 18 || minor > 20 {
return nil, fmt.Errorf("requires go version 1.18 through 1.20, got go%d.%d", major, minor)
if major != 1 || minor < 16 || minor > 18 {
return nil, fmt.Errorf("requires go version 1.16 through 1.18, got go%d.%d", major, minor)
}
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
+2 -4
View File
@@ -1,8 +1,6 @@
package builder
import (
"errors"
"io/fs"
"io/ioutil"
"os"
"os/exec"
@@ -19,13 +17,13 @@ import (
func getClangHeaderPath(TINYGOROOT string) string {
// Check whether we're running from the source directory.
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
if _, err := os.Stat(path); !os.IsNotExist(err) {
return path
}
// Check whether we're running from the installation directory.
path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
if _, err := os.Stat(path); !os.IsNotExist(err) {
return path
}
+2 -2
View File
@@ -13,7 +13,7 @@ import (
"debug/elf"
"encoding/binary"
"fmt"
"os"
"io/ioutil"
"sort"
"strings"
)
@@ -189,5 +189,5 @@ func makeESPFirmareImage(infile, outfile, format string) error {
}
// Write the image to the output file.
return os.WriteFile(outfile, outf.Bytes(), 0666)
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
}
+12 -30
View File
@@ -1,8 +1,7 @@
package builder
import (
"errors"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -29,7 +28,7 @@ type Library struct {
sourceDir func() string
// 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.
crt1Source string
@@ -95,7 +94,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
target := config.Triple()
if l.makeHeaders != nil {
if _, err = os.Stat(headerPath); err != nil {
temporaryHeaderPath, err := os.MkdirTemp(outdir, "include.tmp*")
temporaryHeaderPath, err := ioutil.TempDir(outdir, "include.tmp*")
if err != nil {
return nil, nil, err
}
@@ -111,10 +110,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
err = os.Rename(temporaryHeaderPath, headerPath)
if err != nil {
switch {
case errors.Is(err, fs.ErrExist):
case os.IsExist(err):
// Another invocation of TinyGo also seems to have already created the headers.
case runtime.GOOS == "windows" && errors.Is(err, fs.ErrPermission):
case runtime.GOOS == "windows" && os.IsPermission(err):
// On Windows, a rename with a destination directory that already
// exists does not result in an IsExist error, but rather in an
// access denied error. To be sure, check for this case by checking
@@ -142,7 +141,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// Note: -fdebug-prefix-map is necessary to make the output archive
// reproducible. Otherwise the temporary directory is stored in the archive
// 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()
if cpu != "" {
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
@@ -155,15 +154,8 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
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.Split(target, "-")[2] == "linux" {
args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
} else {
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
}
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
}
if strings.HasPrefix(target, "avr") {
// AVR defaults to C float and double both being 32-bit. This deviates
@@ -173,10 +165,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
args = append(args, "-mdouble=64")
}
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-") {
args = append(args, "-march=rv64gc")
args = append(args, "-march=rv64gc", "-mabi=lp64")
}
if strings.HasPrefix(target, "xtensa") {
// Hack to work around an issue in the Xtensa port:
@@ -197,7 +189,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
defer once.Do(unlock)
// Create an archive of all object files.
f, err := os.CreateTemp(outdir, "libc.a.tmp*")
f, err := ioutil.TempFile(outdir, "libc.a.tmp*")
if err != nil {
return err
}
@@ -222,11 +214,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
// archive job above, so must be run first.
paths, err := l.librarySources(target)
if err != nil {
return nil, nil, err
}
for _, path := range paths {
for _, path := range l.librarySources(target) {
// Strip leading "../" parts off the path.
cleanpath := path
for strings.HasPrefix(cleanpath, "../") {
@@ -242,9 +230,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
var compileArgs []string
compileArgs = append(compileArgs, args...)
compileArgs = append(compileArgs, "-o", objpath, srcpath)
if config.Options.PrintCommands != nil {
config.Options.PrintCommands("clang", compileArgs...)
}
err := runCCompiler(compileArgs...)
if err != nil {
return &commandError{"failed to build", srcpath, err}
@@ -265,15 +250,12 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
run: func(*compileJob) error {
var compileArgs []string
compileArgs = append(compileArgs, args...)
tmpfile, err := os.CreateTemp(outdir, "crt1.o.tmp*")
tmpfile, err := ioutil.TempFile(outdir, "crt1.o.tmp*")
if err != nil {
return err
}
tmpfile.Close()
compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath)
if config.Options.PrintCommands != nil {
config.Options.PrintCommands("clang", compileArgs...)
}
err = runCCompiler(compileArgs...)
if err != nil {
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.
+6 -18
View File
@@ -1,7 +1,6 @@
package builder
import (
"fmt"
"io"
"os"
"path/filepath"
@@ -32,9 +31,9 @@ var MinGW = Library{
// No flags necessary because there are no files to compile.
return nil
},
librarySources: func(target string) ([]string, error) {
librarySources: func(target string) []string {
// 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
// compile these files.
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
func makeMinGWExtraLibs(tmpdir string) []*compileJob {
var jobs []*compileJob
root := goenv.Get("TINYGOROOT")
// 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{
"kernel32.def.in",
"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-filesystem-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,
run: func(job *compileJob) error {
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") {
// .in files need to be preprocessed by a preprocessor (-E)
// first.
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", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
if err != nil {
return err
}
}
return link("ld.lld", "-m", emulation, "-o", outpath, defpath)
return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath)
},
}
jobs = append(jobs, job)
+9 -27
View File
@@ -3,15 +3,14 @@ package builder
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
)
var Musl = Library{
@@ -36,7 +35,7 @@ var Musl = Library{
filepath.Join(muslDir, "include", "alltypes.h.in"),
}
for _, infile := range infiles {
data, err := os.ReadFile(infile)
data, err := ioutil.ReadFile(infile)
if err != nil {
return err
}
@@ -64,7 +63,7 @@ var Musl = Library{
if err != nil {
return err
}
data, err := os.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
data, err := ioutil.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
if err != nil {
return err
}
@@ -79,7 +78,7 @@ var Musl = Library{
cflags: func(target, headerPath string) []string {
arch := compileopts.MuslArchitecture(target)
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl")
cflags := []string{
return []string{
"-std=c99", // same as in musl
"-D_XOPEN_SOURCE=700", // same as in musl
// Musl triggers some warnings and we don't want to show any
@@ -91,8 +90,6 @@ var Musl = Library{
"-Wno-shift-op-parentheses",
"-Wno-ignored-attributes",
"-Wno-string-plus-int",
"-Wno-ignored-pragmas",
"-Wno-tautological-constant-out-of-range-compare",
"-Qunused-arguments",
// Select include dirs. Don't include standard library includes
// (that would introduce host dependencies and other complications),
@@ -106,15 +103,9 @@ var Musl = Library{
"-I" + muslDir + "/include",
"-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") },
librarySources: func(target string) ([]string, error) {
librarySources: func(target string) []string {
arch := compileopts.MuslArchitecture(target)
globs := []string{
"env/*.c",
@@ -124,23 +115,17 @@ var Musl = Library{
"internal/libc.c",
"internal/syscall_ret.c",
"internal/vdso.c",
"legacy/*.c",
"malloc/*.c",
"malloc/mallocng/*.c",
"mman/*.c",
"math/*.c",
"signal/*.c",
"stdio/*.c",
"string/*.c",
"thread/" + arch + "/*.s",
"thread/" + arch + "/*.c",
"thread/*.c",
"time/*.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
seenSources := map[string]struct{}{}
@@ -154,16 +139,13 @@ var Musl = Library{
// > ErrBadPattern, when pattern is malformed.
// So the only possible error is when the (statically defined)
// pattern is wrong. In other words, a programming bug.
return nil, fmt.Errorf("musl: could not glob source dirs: %w", err)
}
if len(matches) == 0 {
return nil, fmt.Errorf("musl: did not find any files for pattern %#v", pattern)
panic("could not glob source dirs: " + err.Error())
}
for _, match := range matches {
relpath, err := filepath.Rel(basepath, match)
if err != nil {
// Not sure if this is even possible.
return nil, err
panic(err)
}
// Make sure architecture specific files override generic files.
id := strings.ReplaceAll(relpath, "/"+arch+"/", "/")
@@ -175,7 +157,7 @@ var Musl = Library{
sources = append(sources, relpath)
}
}
return sources, nil
return sources
},
crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c
}
+2 -2
View File
@@ -2,7 +2,7 @@ package builder
import (
"fmt"
"io"
"io/ioutil"
"os/exec"
"github.com/tinygo-org/tinygo/compileopts"
@@ -18,7 +18,7 @@ func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string)
}
cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
cmd.Stdout = io.Discard
cmd.Stdout = ioutil.Discard
err := cmd.Run()
if err != nil {
return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
+2 -2
View File
@@ -2,7 +2,7 @@ package builder
import (
"debug/elf"
"io"
"io/ioutil"
"os"
"sort"
@@ -87,7 +87,7 @@ func extractROM(path string) (uint64, []byte, error) {
// Pad the difference
rom = append(rom, make([]byte, diff)...)
}
data, err := io.ReadAll(prog.Open())
data, err := ioutil.ReadAll(prog.Open())
if err != nil {
return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err}
}
+207 -384
View File
@@ -19,404 +19,227 @@ var Picolibc = Library{
return f.Close()
},
cflags: func(target, headerPath string) []string {
newlibDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib")
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
return []string{
"-Werror",
"-Wall",
"-std=gnu11",
"-D_COMPILING_NEWLIB",
"-D_HAVE_ALIAS_ATTRIBUTE",
"-DHAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO",
"-DPOSIX_IO",
"-D_IEEE_LIBM",
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
"-D__OBSOLETE_MATH_DOUBLE=0",
"-D_WANT_IO_C99_FORMATS",
"-nostdlibinc",
"-isystem", newlibDir + "/libc/include",
"-I" + newlibDir + "/libc/tinystdio",
"-I" + newlibDir + "/libm/common",
"-isystem", picolibcDir + "/include",
"-I" + picolibcDir + "/tinystdio",
"-I" + headerPath,
}
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
librarySources: func(target string) ([]string, error) {
return picolibcSources, nil
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") },
librarySources: func(target string) []string {
return picolibcSources
},
}
var picolibcSources = []string{
"../../picolibc-stdio.c",
"../../../picolibc-stdio.c",
// srcs_tinystdio
"libc/tinystdio/asprintf.c",
"libc/tinystdio/bufio.c",
"libc/tinystdio/clearerr.c",
"libc/tinystdio/ecvt_r.c",
"libc/tinystdio/ecvt.c",
"libc/tinystdio/ecvtf_r.c",
"libc/tinystdio/ecvtf.c",
"libc/tinystdio/fcvt.c",
"libc/tinystdio/fcvt_r.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/feof.c",
"libc/tinystdio/ferror.c",
"libc/tinystdio/fflush.c",
"libc/tinystdio/fgetc.c",
"libc/tinystdio/fgets.c",
"libc/tinystdio/fileno.c",
"libc/tinystdio/filestrget.c",
"libc/tinystdio/filestrput.c",
"libc/tinystdio/filestrputalloc.c",
"libc/tinystdio/fmemopen.c",
"libc/tinystdio/fprintf.c",
"libc/tinystdio/fputc.c",
"libc/tinystdio/fputs.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/fseek.c",
"libc/tinystdio/fseeko.c",
"libc/tinystdio/ftell.c",
"libc/tinystdio/ftello.c",
"libc/tinystdio/fwrite.c",
"libc/tinystdio/getchar.c",
"libc/tinystdio/gets.c",
"libc/tinystdio/matchcaseprefix.c",
"libc/tinystdio/mktemp.c",
"libc/tinystdio/perror.c",
"libc/tinystdio/printf.c",
"libc/tinystdio/putchar.c",
"libc/tinystdio/puts.c",
"libc/tinystdio/rewind.c",
"libc/tinystdio/scanf.c",
"libc/tinystdio/setbuf.c",
"libc/tinystdio/setbuffer.c",
"libc/tinystdio/setlinebuf.c",
"libc/tinystdio/setvbuf.c",
"libc/tinystdio/snprintf.c",
"libc/tinystdio/sprintf.c",
"libc/tinystdio/snprintfd.c",
"libc/tinystdio/snprintff.c",
"libc/tinystdio/sprintff.c",
"libc/tinystdio/sprintfd.c",
"libc/tinystdio/sscanf.c",
"libc/tinystdio/strfromf.c",
"libc/tinystdio/strfromd.c",
"libc/tinystdio/strtof.c",
"libc/tinystdio/strtof_l.c",
"libc/tinystdio/strtod.c",
"libc/tinystdio/strtod_l.c",
"libc/tinystdio/ungetc.c",
"libc/tinystdio/vasprintf.c",
"libc/tinystdio/vfiprintf.c",
"libc/tinystdio/vfprintf.c",
"libc/tinystdio/vfprintff.c",
"libc/tinystdio/vfscanf.c",
"libc/tinystdio/vfiscanf.c",
"libc/tinystdio/vfscanff.c",
"libc/tinystdio/vprintf.c",
"libc/tinystdio/vscanf.c",
"libc/tinystdio/vsscanf.c",
"libc/tinystdio/vsnprintf.c",
"libc/tinystdio/vsprintf.c",
"tinystdio/asprintf.c",
"tinystdio/atod_engine.c",
"tinystdio/atod_ryu.c",
"tinystdio/atof_engine.c",
"tinystdio/atof_ryu.c",
//"tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
"tinystdio/clearerr.c",
"tinystdio/compare_exchange.c",
"tinystdio/dtoa_data.c",
"tinystdio/dtoa_engine.c",
"tinystdio/dtoa_ryu.c",
"tinystdio/ecvtbuf.c",
"tinystdio/ecvt.c",
"tinystdio/ecvt_data.c",
"tinystdio/ecvtfbuf.c",
"tinystdio/ecvtf.c",
"tinystdio/ecvtf_data.c",
"tinystdio/exchange.c",
//"tinystdio/fclose.c", // posix-io
"tinystdio/fcvtbuf.c",
"tinystdio/fcvt.c",
"tinystdio/fcvtfbuf.c",
"tinystdio/fcvtf.c",
"tinystdio/fdevopen.c",
//"tinystdio/fdopen.c", // posix-io
"tinystdio/feof.c",
"tinystdio/ferror.c",
"tinystdio/fflush.c",
"tinystdio/fgetc.c",
"tinystdio/fgets.c",
"tinystdio/fileno.c",
"tinystdio/filestrget.c",
"tinystdio/filestrputalloc.c",
"tinystdio/filestrput.c",
//"tinystdio/fopen.c", // posix-io
"tinystdio/fprintf.c",
"tinystdio/fputc.c",
"tinystdio/fputs.c",
"tinystdio/fread.c",
"tinystdio/fscanf.c",
"tinystdio/fseek.c",
"tinystdio/ftell.c",
"tinystdio/ftoa_data.c",
"tinystdio/ftoa_engine.c",
"tinystdio/ftoa_ryu.c",
"tinystdio/fwrite.c",
"tinystdio/gcvtbuf.c",
"tinystdio/gcvt.c",
"tinystdio/gcvtfbuf.c",
"tinystdio/gcvtf.c",
"tinystdio/getchar.c",
"tinystdio/gets.c",
"tinystdio/matchcaseprefix.c",
"tinystdio/perror.c",
//"tinystdio/posixiob.c", // posix-io
//"tinystdio/posixio.c", // posix-io
"tinystdio/printf.c",
"tinystdio/putchar.c",
"tinystdio/puts.c",
"tinystdio/ryu_divpow2.c",
"tinystdio/ryu_log10.c",
"tinystdio/ryu_log2pow5.c",
"tinystdio/ryu_pow5bits.c",
"tinystdio/ryu_table.c",
"tinystdio/ryu_umul128.c",
"tinystdio/scanf.c",
"tinystdio/setbuf.c",
"tinystdio/setvbuf.c",
//"tinystdio/sflags.c", // posix-io
"tinystdio/snprintf.c",
"tinystdio/snprintfd.c",
"tinystdio/snprintff.c",
"tinystdio/sprintf.c",
"tinystdio/sprintfd.c",
"tinystdio/sprintff.c",
"tinystdio/sscanf.c",
"tinystdio/strfromd.c",
"tinystdio/strfromf.c",
"tinystdio/strtod.c",
"tinystdio/strtod_l.c",
"tinystdio/strtof.c",
//"tinystdio/strtold.c", // have_long_double and not long_double_equals_double
//"tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
"tinystdio/ungetc.c",
"tinystdio/vasprintf.c",
"tinystdio/vfiprintf.c",
"tinystdio/vfiscanf.c",
"tinystdio/vfprintf.c",
"tinystdio/vfprintff.c",
"tinystdio/vfscanf.c",
"tinystdio/vfscanff.c",
"tinystdio/vprintf.c",
"tinystdio/vscanf.c",
"tinystdio/vsnprintf.c",
"tinystdio/vsprintf.c",
"tinystdio/vsscanf.c",
"libc/string/bcmp.c",
"libc/string/bcopy.c",
"libc/string/bzero.c",
"libc/string/explicit_bzero.c",
"libc/string/ffsl.c",
"libc/string/ffsll.c",
"libc/string/fls.c",
"libc/string/flsl.c",
"libc/string/flsll.c",
"libc/string/gnu_basename.c",
"libc/string/index.c",
"libc/string/memccpy.c",
"libc/string/memchr.c",
"libc/string/memcmp.c",
"libc/string/memcpy.c",
"libc/string/memmem.c",
"libc/string/memmove.c",
"libc/string/mempcpy.c",
"libc/string/memrchr.c",
"libc/string/memset.c",
"libc/string/rawmemchr.c",
"libc/string/rindex.c",
"libc/string/stpcpy.c",
"libc/string/stpncpy.c",
"libc/string/strcasecmp.c",
"libc/string/strcasecmp_l.c",
"libc/string/strcasestr.c",
"libc/string/strcat.c",
"libc/string/strchr.c",
"libc/string/strchrnul.c",
"libc/string/strcmp.c",
"libc/string/strcoll.c",
"libc/string/strcoll_l.c",
"libc/string/strcpy.c",
"libc/string/strcspn.c",
"libc/string/strdup.c",
"libc/string/strerror.c",
"libc/string/strerror_r.c",
"libc/string/strlcat.c",
"libc/string/strlcpy.c",
"libc/string/strlen.c",
"libc/string/strlwr.c",
"libc/string/strncasecmp.c",
"libc/string/strncasecmp_l.c",
"libc/string/strncat.c",
"libc/string/strncmp.c",
"libc/string/strncpy.c",
"libc/string/strndup.c",
"libc/string/strnlen.c",
"libc/string/strnstr.c",
"libc/string/strpbrk.c",
"libc/string/strrchr.c",
"libc/string/strsep.c",
"libc/string/strsignal.c",
"libc/string/strspn.c",
"libc/string/strstr.c",
"libc/string/strtok.c",
"libc/string/strtok_r.c",
"libc/string/strupr.c",
"libc/string/strverscmp.c",
"libc/string/strxfrm.c",
"libc/string/strxfrm_l.c",
"libc/string/swab.c",
"libc/string/timingsafe_bcmp.c",
"libc/string/timingsafe_memcmp.c",
"libc/string/u_strerr.c",
"libc/string/wcpcpy.c",
"libc/string/wcpncpy.c",
"libc/string/wcscasecmp.c",
"libc/string/wcscasecmp_l.c",
"libc/string/wcscat.c",
"libc/string/wcschr.c",
"libc/string/wcscmp.c",
"libc/string/wcscoll.c",
"libc/string/wcscoll_l.c",
"libc/string/wcscpy.c",
"libc/string/wcscspn.c",
"libc/string/wcsdup.c",
"libc/string/wcslcat.c",
"libc/string/wcslcpy.c",
"libc/string/wcslen.c",
"libc/string/wcsncasecmp.c",
"libc/string/wcsncasecmp_l.c",
"libc/string/wcsncat.c",
"libc/string/wcsncmp.c",
"libc/string/wcsncpy.c",
"libc/string/wcsnlen.c",
"libc/string/wcspbrk.c",
"libc/string/wcsrchr.c",
"libc/string/wcsspn.c",
"libc/string/wcsstr.c",
"libc/string/wcstok.c",
"libc/string/wcswidth.c",
"libc/string/wcsxfrm.c",
"libc/string/wcsxfrm_l.c",
"libc/string/wcwidth.c",
"libc/string/wmemchr.c",
"libc/string/wmemcmp.c",
"libc/string/wmemcpy.c",
"libc/string/wmemmove.c",
"libc/string/wmempcpy.c",
"libc/string/wmemset.c",
"libc/string/xpg_strerror_r.c",
"libm/common/sf_finite.c",
"libm/common/sf_copysign.c",
"libm/common/sf_modf.c",
"libm/common/sf_scalbn.c",
"libm/common/sf_cbrt.c",
"libm/common/sf_exp10.c",
"libm/common/sf_expm1.c",
"libm/common/sf_ilogb.c",
"libm/common/sf_infinity.c",
"libm/common/sf_isinf.c",
"libm/common/sf_isinff.c",
"libm/common/sf_isnan.c",
"libm/common/sf_isnanf.c",
"libm/common/sf_issignaling.c",
"libm/common/sf_log1p.c",
"libm/common/sf_nan.c",
"libm/common/sf_nextafter.c",
"libm/common/sf_pow10.c",
"libm/common/sf_rint.c",
"libm/common/sf_logb.c",
"libm/common/sf_fdim.c",
"libm/common/sf_fma.c",
"libm/common/sf_fmax.c",
"libm/common/sf_fmin.c",
"libm/common/sf_fpclassify.c",
"libm/common/sf_lrint.c",
"libm/common/sf_llrint.c",
"libm/common/sf_lround.c",
"libm/common/sf_llround.c",
"libm/common/sf_nearbyint.c",
"libm/common/sf_remquo.c",
"libm/common/sf_round.c",
"libm/common/sf_scalbln.c",
"libm/common/sf_trunc.c",
"libm/common/sf_exp.c",
"libm/common/sf_exp2.c",
"libm/common/sf_exp2_data.c",
"libm/common/sf_log.c",
"libm/common/sf_log_data.c",
"libm/common/sf_log2.c",
"libm/common/sf_log2_data.c",
"libm/common/sf_pow_log2_data.c",
"libm/common/sf_pow.c",
"libm/common/s_finite.c",
"libm/common/s_copysign.c",
"libm/common/s_modf.c",
"libm/common/s_scalbn.c",
"libm/common/s_cbrt.c",
"libm/common/s_exp10.c",
"libm/common/s_expm1.c",
"libm/common/s_ilogb.c",
"libm/common/s_infinity.c",
"libm/common/s_iseqsig.c",
"libm/common/s_isinf.c",
"libm/common/s_isinfd.c",
"libm/common/s_isnan.c",
"libm/common/s_isnand.c",
"libm/common/s_issignaling.c",
"libm/common/s_log1p.c",
"libm/common/s_nan.c",
"libm/common/s_nextafter.c",
"libm/common/s_pow10.c",
"libm/common/s_rint.c",
"libm/common/s_logb.c",
"libm/common/s_log2.c",
"libm/common/s_fdim.c",
"libm/common/s_fma.c",
"libm/common/s_fmax.c",
"libm/common/s_fmin.c",
"libm/common/s_fpclassify.c",
"libm/common/s_getpayload.c",
"libm/common/s_lrint.c",
"libm/common/s_llrint.c",
"libm/common/s_lround.c",
"libm/common/s_llround.c",
"libm/common/s_nearbyint.c",
"libm/common/s_remquo.c",
"libm/common/s_round.c",
"libm/common/s_scalbln.c",
"libm/common/s_signbit.c",
"libm/common/s_trunc.c",
"libm/common/exp.c",
"libm/common/exp2.c",
"libm/common/exp_data.c",
"libm/common/math_err_with_errno.c",
"libm/common/math_err_uflow.c",
"libm/common/math_err_oflow.c",
"libm/common/math_err_divzero.c",
"libm/common/math_err_invalid.c",
"libm/common/math_err_may_uflow.c",
"libm/common/math_err_check_uflow.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_data.c",
"libm/common/log2.c",
"libm/common/log2_data.c",
"libm/common/pow.c",
"libm/common/pow_log_data.c",
"libm/math/k_cos.c",
"libm/math/k_rem_pio2.c",
"libm/math/k_sin.c",
"libm/math/k_tan.c",
"libm/math/kf_cos.c",
"libm/math/kf_rem_pio2.c",
"libm/math/kf_sin.c",
"libm/math/kf_tan.c",
"libm/math/s_acos.c",
"libm/math/s_acosh.c",
"libm/math/s_asin.c",
"libm/math/s_asinh.c",
"libm/math/s_atan.c",
"libm/math/s_atan2.c",
"libm/math/s_atanh.c",
"libm/math/s_ceil.c",
"libm/math/s_cos.c",
"libm/math/s_cosh.c",
"libm/math/s_drem.c",
"libm/math/s_erf.c",
"libm/math/s_exp.c",
"libm/math/s_exp2.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_atan.c",
"libm/math/sf_atan2.c",
"libm/math/sf_atanh.c",
"libm/math/sf_ceil.c",
"libm/math/sf_cos.c",
"libm/math/sf_cosh.c",
"libm/math/sf_drem.c",
"libm/math/sf_erf.c",
"libm/math/sf_exp.c",
"libm/math/sf_exp2.c",
"libm/math/sf_fabs.c",
"libm/math/sf_floor.c",
"libm/math/sf_fmod.c",
"libm/math/sf_frexp.c",
"libm/math/sf_gamma.c",
"libm/math/sf_hypot.c",
"libm/math/sf_j0.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_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_tanh.c",
"libm/math/sf_tgamma.c",
"libm/math/sr_lgamma.c",
"libm/math/srf_lgamma.c",
"string/bcmp.c",
"string/bcopy.c",
"string/bzero.c",
"string/explicit_bzero.c",
"string/ffsl.c",
"string/ffsll.c",
"string/fls.c",
"string/flsl.c",
"string/flsll.c",
"string/gnu_basename.c",
"string/index.c",
"string/memccpy.c",
"string/memchr.c",
"string/memcmp.c",
"string/memcpy.c",
"string/memmem.c",
"string/memmove.c",
"string/mempcpy.c",
"string/memrchr.c",
"string/memset.c",
"string/rawmemchr.c",
"string/rindex.c",
"string/stpcpy.c",
"string/stpncpy.c",
"string/strcasecmp.c",
"string/strcasecmp_l.c",
"string/strcasestr.c",
"string/strcat.c",
"string/strchr.c",
"string/strchrnul.c",
"string/strcmp.c",
"string/strcoll.c",
"string/strcoll_l.c",
"string/strcpy.c",
"string/strcspn.c",
"string/strdup.c",
"string/strerror.c",
"string/strerror_r.c",
"string/strlcat.c",
"string/strlcpy.c",
"string/strlen.c",
"string/strlwr.c",
"string/strncasecmp.c",
"string/strncasecmp_l.c",
"string/strncat.c",
"string/strncmp.c",
"string/strncpy.c",
"string/strndup.c",
"string/strnlen.c",
"string/strnstr.c",
"string/strpbrk.c",
"string/strrchr.c",
"string/strsep.c",
"string/strsignal.c",
"string/strspn.c",
"string/strstr.c",
"string/strtok.c",
"string/strtok_r.c",
"string/strupr.c",
"string/strverscmp.c",
"string/strxfrm.c",
"string/strxfrm_l.c",
"string/swab.c",
"string/timingsafe_bcmp.c",
"string/timingsafe_memcmp.c",
"string/u_strerr.c",
"string/wcpcpy.c",
"string/wcpncpy.c",
"string/wcscasecmp.c",
"string/wcscasecmp_l.c",
"string/wcscat.c",
"string/wcschr.c",
"string/wcscmp.c",
"string/wcscoll.c",
"string/wcscoll_l.c",
"string/wcscpy.c",
"string/wcscspn.c",
"string/wcsdup.c",
"string/wcslcat.c",
"string/wcslcpy.c",
"string/wcslen.c",
"string/wcsncasecmp.c",
"string/wcsncasecmp_l.c",
"string/wcsncat.c",
"string/wcsncmp.c",
"string/wcsncpy.c",
"string/wcsnlen.c",
"string/wcspbrk.c",
"string/wcsrchr.c",
"string/wcsspn.c",
"string/wcsstr.c",
"string/wcstok.c",
"string/wcswidth.c",
"string/wcsxfrm.c",
"string/wcsxfrm_l.c",
"string/wcwidth.c",
"string/wmemchr.c",
"string/wmemcmp.c",
"string/wmemcpy.c",
"string/wmemmove.c",
"string/wmempcpy.c",
"string/wmemset.c",
"string/xpg_strerror_r.c",
}
+19 -147
View File
@@ -118,12 +118,16 @@ var (
// pack: data created when storing a constant in an interface for example
// string: buffer behind strings
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
// Reflect sidetables. Created by the reflect lowering pass.
// See src/reflect/sidetables.go.
reflectDataRegexp = regexp.MustCompile(`^reflect\.[a-zA-Z]+Sidetable$`)
)
// readProgramSizeFromDWARF reads the source location for each line of code and
// each variable in the program, as far as this is stored in the DWARF debug
// information.
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone bool) ([]addressLine, error) {
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLine, error) {
r := data.Reader()
var lines []*dwarf.LineFile
var addresses []addressLine
@@ -165,7 +169,7 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone
return nil, err
}
if prevLineEntry.EndSequence && lineEntry.Address == 0 && skipTombstone {
if prevLineEntry.EndSequence && lineEntry.Address == 0 {
// Tombstone value. This symbol has been removed, for
// example by the --gc-sections linker flag. It is still
// here in the debug information because the linker can't
@@ -174,10 +178,6 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone
// skipped.
// For more details, see (among others):
// https://reviews.llvm.org/D84825
// The value 0 can however really occur in object files,
// that typically start at address 0. So don't skip
// tombstone values in object files (like when parsing MachO
// files).
for {
err := lr.Next(&lineEntry)
if err != nil {
@@ -256,65 +256,6 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone
return addresses, nil
}
// Read a MachO object file and return a line table.
// Also return an index from symbol name to start address in the line table.
func readMachOSymbolAddresses(path string) (map[string]int, []addressLine, error) {
// Some constants from mach-o/nlist.h
// See: https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
const (
N_STAB = 0xe0
N_TYPE = 0x0e // bitmask for N_TYPE field
N_SECT = 0xe // one of the possible type in the N_TYPE field
)
// Read DWARF from the given object file.
file, err := macho.Open(path)
if err != nil {
return nil, nil, err
}
defer file.Close()
dwarf, err := file.DWARF()
if err != nil {
return nil, nil, err
}
lines, err := readProgramSizeFromDWARF(dwarf, 0, false)
if err != nil {
return nil, nil, err
}
// Make a map from start addresses to indices in the line table (because the
// line table is a slice, not a map).
addressToLine := make(map[uint64]int, len(lines))
for i, line := range lines {
if _, ok := addressToLine[line.Address]; ok {
addressToLine[line.Address] = -1
continue
}
addressToLine[line.Address] = i
}
// Make a map that for each symbol gives the start index in the line table.
addresses := make(map[string]int, len(addressToLine))
for _, symbol := range file.Symtab.Syms {
if symbol.Type&N_STAB != 0 {
continue // STABS entry, ignore
}
if symbol.Type&0x0e != N_SECT {
continue // undefined symbol
}
if index, ok := addressToLine[symbol.Value]; ok && index >= 0 {
if _, ok := addresses[symbol.Name]; ok {
// There is a duplicate. Mark it as unavailable.
addresses[symbol.Name] = -1
continue
}
addresses[symbol.Name] = index
}
}
return addresses, lines, nil
}
// loadProgramSize calculate a program/data size breakdown of each package for a
// given ELF file.
// If the file doesn't contain DWARF debug information, the returned program
@@ -337,7 +278,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
// Read DWARF information. The error is intentionally ignored.
data, _ := file.DWARF()
if data != nil {
addresses, err = readProgramSizeFromDWARF(data, 0, true)
addresses, err = readProgramSizeFromDWARF(data, 0)
if err != nil {
// However, _do_ report an error here. Something must have gone
// wrong while trying to parse DWARF data.
@@ -371,7 +312,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
if section.Flags&elf.SHF_ALLOC == 0 {
continue
}
if packageSymbolRegexp.MatchString(symbol.Name) {
if packageSymbolRegexp.MatchString(symbol.Name) || reflectDataRegexp.MatchString(symbol.Name) {
addresses = append(addresses, addressLine{
Address: symbol.Value,
Length: symbol.Size,
@@ -429,6 +370,11 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
}
}
} else if file, err := macho.NewFile(f); err == nil {
// TODO: read DWARF information. On MacOS, DWARF debug information isn't
// stored in the executable but stays in the object files. The
// executable does however contain the object file paths that contain
// debug information.
// Read segments, for use while reading through sections.
segments := map[string]*macho.Segment{}
for _, load := range file.Loads {
@@ -475,86 +421,11 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
})
}
}
// Read DWARF information.
// The data isn't stored directly in the binary as in most executable
// formats. Instead, it is left in the object files that were used as a
// basis for linking. The executable does however contain STABS debug
// information that points to the source object file and is used by
// debuggers.
// For more information:
// http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme
var objSymbolNames map[string]int
var objAddresses []addressLine
var previousSymbol macho.Symbol
for _, symbol := range file.Symtab.Syms {
// STABS constants, from mach-o/stab.h:
// https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/stab.h.auto.html
const (
N_GSYM = 0x20
N_FUN = 0x24
N_STSYM = 0x26
N_SO = 0x64
N_OSO = 0x66
)
if symbol.Type == N_OSO {
// Found an object file. Now try to parse it.
objSymbolNames, objAddresses, err = readMachOSymbolAddresses(symbol.Name)
if err != nil && sizesDebug {
// Errors are normally ignored. If there is an error, it's
// simply treated as that the DWARF is not available.
fmt.Fprintf(os.Stderr, "could not read DWARF from file %s: %s\n", symbol.Name, err)
}
} else if symbol.Type == N_FUN {
// Found a function.
// The way this is encoded is a bit weird. MachO symbols don't
// have a length. What I've found is that the length is encoded
// by first having a N_FUN symbol as usual, and then having a
// symbol with a zero-length name that has the value not set to
// the address of the symbol but to the length. So in order to
// get both the address and the length, we look for a symbol
// with a name followed by a symbol without a name.
if symbol.Name == "" && previousSymbol.Type == N_FUN && previousSymbol.Name != "" {
// Functions are encoded as many small chunks in the line
// table (one or a few instructions per source line). But
// the symbol length covers the whole symbols, over many
// lines and possibly including inlined functions. So we
// continue to iterate through the objAddresses slice until
// we've found all the source lines that are part of this
// symbol.
address := previousSymbol.Value
length := symbol.Value
if index, ok := objSymbolNames[previousSymbol.Name]; ok && index >= 0 {
for length > 0 {
line := objAddresses[index]
line.Address = address
if line.Length > length {
// Line extends beyond the end of te symbol?
// Weird, shouldn't happen.
break
}
addresses = append(addresses, line)
index++
length -= line.Length
address += line.Length
}
}
}
} else if symbol.Type == N_GSYM || symbol.Type == N_STSYM {
// Global variables.
if index, ok := objSymbolNames[symbol.Name]; ok {
address := objAddresses[index]
address.Address = symbol.Value
addresses = append(addresses, address)
}
}
previousSymbol = symbol
}
} else if file, err := pe.NewFile(f); err == nil {
// Read DWARF information. The error is intentionally ignored.
data, _ := file.DWARF()
if data != nil {
addresses, err = readProgramSizeFromDWARF(data, 0, true)
addresses, err = readProgramSizeFromDWARF(data, 0)
if err != nil {
// However, _do_ report an error here. Something must have gone
// wrong while trying to parse DWARF data.
@@ -624,9 +495,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
const codeOffset = 0x8000_0000_0000_0000
// Read DWARF information. The error is intentionally ignored.
data, _ := file.DWARF()
data, err := file.DWARF()
if data != nil {
addresses, err = readProgramSizeFromDWARF(data, codeOffset, true)
addresses, err = readProgramSizeFromDWARF(data, codeOffset)
if err != nil {
// However, _do_ report an error here. Something must have gone
// wrong while trying to parse DWARF data.
@@ -832,8 +703,9 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
} else if packageSymbolRegexp.MatchString(path) {
// Parse symbol names like main$alloc or runtime$string.
packagePath = path[:strings.LastIndex(path, "$")]
} else if path == "<Go type>" {
packagePath = "Go types"
} else if reflectDataRegexp.MatchString(path) {
// Parse symbol names like reflect.structTypesSidetable.
packagePath = "Go reflect data"
} else if path == "<Go interface assert>" {
// Interface type assert, generated by the interface lowering pass.
packagePath = "Go interface assert"
+2 -1
View File
@@ -1,4 +1,5 @@
//go:build byollvm
// +build byollvm
package builder
@@ -28,7 +29,7 @@ const hasBuiltinTools = true
func RunTool(tool string, args ...string) error {
linker := "elf"
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"
} else if args[0] == "-flavor" {
linker = args[1]
+1
View File
@@ -1,4 +1,5 @@
//go:build !byollvm
// +build !byollvm
package builder
+2 -2
View File
@@ -10,7 +10,7 @@ package builder
import (
"bytes"
"encoding/binary"
"os"
"io/ioutil"
"strconv"
)
@@ -26,7 +26,7 @@ func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
if err != nil {
return err
}
return os.WriteFile(outfile, output, 0644)
return ioutil.WriteFile(outfile, output, 0644)
}
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
+31 -83
View File
@@ -32,7 +32,6 @@ type cgoPackage struct {
errors []error
currentDir string // current working directory
packageDir string // full path to the package to process
importPath string
fset *token.FileSet
tokenFiles map[string]*token.File
definedGlobally map[string]ast.Node
@@ -40,15 +39,12 @@ type cgoPackage struct {
cflags []string // CFlags from #cgo lines
ldflags []string // LDFlags from #cgo lines
visitedFiles map[string][]byte
cgoHeaders []string
}
// cgoFile holds information only for a single Go file (with one or more
// `import "C"` statements).
type cgoFile struct {
*cgoPackage
file *ast.File
index int
defined map[string]ast.Node
names map[string]clangCursor
}
@@ -86,8 +82,6 @@ var cgoAliases = map[string]string{
"C.uint32_t": "uint32",
"C.uint64_t": "uint64",
"C.uintptr_t": "uintptr",
"C.float": "float32",
"C.double": "float64",
}
// builtinAliases are handled specially because they only exist on the Go side
@@ -164,10 +158,9 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
// hashes of the accessed C header files. If there is one or more error, it
// returns these in the []error slice but still modifies the AST.
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
p := &cgoPackage{
currentDir: dir,
importPath: importPath,
fset: fset,
tokenFiles: map[string]*token.File{},
definedGlobally: map[string]ast.Node{},
@@ -217,13 +210,13 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
}
}
// Patch some types, for example *C.char in C.CString.
cf := p.newCGoFile(nil, -1) // dummy *cgoFile for the walker
cf := p.newCGoFile()
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
return cf.walker(cursor, nil)
}, nil)
// Find `import "C"` C fragments in the file.
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
cgoHeaders := make([]string, len(files)) // combined CGo header fragment for each file
for i, f := range files {
var cgoHeader string
for i := 0; i < len(f.Decls); i++ {
@@ -282,7 +275,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
cgoHeader += fragment
}
p.cgoHeaders[i] = cgoHeader
cgoHeaders[i] = cgoHeader
}
// Define CFlags that will be used while parsing the package.
@@ -296,7 +289,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
}
// Retrieve types such as C.int, C.longlong, etc from C.
p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
p.newCGoFile().readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
gen := &ast.GenDecl{
TokPos: token.NoPos,
Tok: token.TYPE,
@@ -310,14 +303,8 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// Process CGo imports for each file.
for i, f := range files {
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 := p.newCGoFile()
cf.readNames(cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
for _, name := range builtinAliases {
// Names such as C.int should not be obtained from C.
// This works around an issue in picolibc that has `#define int`
@@ -333,14 +320,12 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated)
return p.generated, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
return p.generated, cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
}
func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
func (p *cgoPackage) newCGoFile() *cgoFile {
return &cgoFile{
cgoPackage: p,
file: file,
index: index,
defined: make(map[string]ast.Node),
names: make(map[string]clangCursor),
}
@@ -508,15 +493,15 @@ func (p *cgoPackage) makeUnionField(typ *elaboratedTypeInfo) *ast.StructType {
// createUnionAccessor creates a function that returns a typed pointer to a
// union field for each field in a union. For example:
//
// func (union *C.union_1) unionfield_d() *float64 {
// return (*float64)(unsafe.Pointer(&union.$union))
// }
// func (union *C.union_1) unionfield_d() *float64 {
// return (*float64)(unsafe.Pointer(&union.$union))
// }
//
// Where C.union_1 is defined as:
//
// type C.union_1 struct{
// $union uint64
// }
// type C.union_1 struct{
// $union uint64
// }
//
// The returned pointer can be used to get or set the field, or get the pointer
// to a subfield.
@@ -632,9 +617,9 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
// createBitfieldGetter creates a bitfield getter function like the following:
//
// func (s *C.struct_foo) bitfield_b() byte {
// return (s.__bitfield_1 >> 5) & 0x1
// }
// func (s *C.struct_foo) bitfield_b() byte {
// return (s.__bitfield_1 >> 5) & 0x1
// }
func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string) {
// The value to return from the getter.
// Not complete: this is just an expression to get the complete field.
@@ -744,15 +729,15 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
// createBitfieldSetter creates a bitfield setter function like the following:
//
// func (s *C.struct_foo) set_bitfield_b(value byte) {
// s.__bitfield_1 = s.__bitfield_1 ^ 0x60 | ((value & 1) << 5)
// }
// func (s *C.struct_foo) set_bitfield_b(value byte) {
// s.__bitfield_1 = s.__bitfield_1 ^ 0x60 | ((value & 1) << 5)
// }
//
// Or the following:
//
// func (s *C.struct_foo) set_bitfield_c(value byte) {
// s.__bitfield_1 = s.__bitfield_1 & 0x3f | (value << 6)
// }
// func (s *C.struct_foo) set_bitfield_c(value byte) {
// s.__bitfield_1 = s.__bitfield_1 & 0x3f | (value << 6)
// }
func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string) {
// The full field with all bitfields.
var field ast.Expr = &ast.SelectorExpr{
@@ -956,9 +941,6 @@ func (p *cgoPackage) isEquivalentAST(a, b ast.Node) bool {
if !ok {
return false
}
if node == nil || b == nil {
return node == b
}
if len(node.List) != len(b.List) {
return false
}
@@ -1135,11 +1117,8 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st
return alias
}
node := f.getASTDeclNode(name, found, iscall)
if node, ok := node.(*ast.FuncDecl); ok {
if !iscall {
return node.Name.Name + "$funcaddr"
}
return node.Name.Name
if _, ok := node.(*ast.FuncDecl); ok && !iscall {
return "C." + name + "$funcaddr"
}
return "C." + name
}
@@ -1163,7 +1142,7 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
// Original cgo reports an error like
// cgo: inconsistent definitions for C.myint
// which is far less helpful.
f.addError(getPos(node), name+" defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
f.addError(getPos(node), "defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
}
f.defined[name] = node
return node
@@ -1171,39 +1150,11 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
// The declaration has no AST node. Create it now.
f.defined[name] = nil
node, extra := f.createASTNode(name, found)
node, elaboratedType := f.createASTNode(name, found)
f.defined[name] = node
f.definedGlobally[name] = node
switch node := node.(type) {
case *ast.FuncDecl:
if strings.HasPrefix(node.Doc.List[0].Text, "//export _Cgo_static_") {
// Static function. Only accessible in the current Go file.
globalName := strings.TrimPrefix(node.Doc.List[0].Text, "//export ")
// Make an alias. Normally this is done using the alias function
// attribute, but MacOS for some reason doesn't support this (even
// though the linker has support for aliases in the form of N_INDR).
// Therefore, create an actual function for MacOS.
var params []string
for _, param := range node.Type.Params.List {
params = append(params, param.Names[0].Name)
}
callInst := fmt.Sprintf("%s(%s);", name, strings.Join(params, ", "))
if node.Type.Results != nil {
callInst = "return " + callInst
}
aliasDeclaration := fmt.Sprintf(`
#ifdef __APPLE__
%s {
%s
}
#else
extern __typeof(%s) %s __attribute__((alias(%#v)));
#endif
`, extra.(string), callInst, name, globalName, name)
f.cgoHeaders[f.index] += "\n\n" + aliasDeclaration
} else {
// Regular (non-static) function.
f.definedGlobally[name] = node
}
f.generated.Decls = append(f.generated.Decls, node)
// Also add a declaration like the following:
// var C.foo$funcaddr unsafe.Pointer
@@ -1211,7 +1162,7 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
Tok: token.VAR,
Specs: []ast.Spec{
&ast.ValueSpec{
Names: []*ast.Ident{{Name: node.Name.Name + "$funcaddr"}},
Names: []*ast.Ident{{Name: "C." + name + "$funcaddr"}},
Type: &ast.SelectorExpr{
X: &ast.Ident{Name: "unsafe"},
Sel: &ast.Ident{Name: "Pointer"},
@@ -1220,10 +1171,8 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
},
})
case *ast.GenDecl:
f.definedGlobally[name] = node
f.generated.Decls = append(f.generated.Decls, node)
case *ast.TypeSpec:
f.definedGlobally[name] = node
f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
Tok: token.TYPE,
Specs: []ast.Spec{node},
@@ -1237,8 +1186,7 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
// If this is a struct or union it may need bitfields or union accessor
// methods.
switch elaboratedType := extra.(type) {
case *elaboratedTypeInfo:
if elaboratedType != nil {
// Add struct bitfields.
for _, bitfield := range elaboratedType.bitfields {
f.createBitfieldGetter(bitfield, "C."+name)
+23 -93
View File
@@ -5,13 +5,13 @@ import (
"flag"
"fmt"
"go/ast"
"go/build"
"go/format"
"go/parser"
"go/token"
"go/types"
"os"
"io/ioutil"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
@@ -22,15 +22,9 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
// normalizeResult normalizes Go source code that comes out of tests across
// platforms and Go versions.
func normalizeResult(t *testing.T, result string) string {
result = strings.ReplaceAll(result, "\r\n", "\n")
// 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 normalizeResult(result string) string {
actual := strings.ReplaceAll(result, "\r\n", "\n")
return actual
}
func TestCGo(t *testing.T) {
@@ -46,6 +40,20 @@ func TestCGo(t *testing.T) {
} {
name := name // avoid a race condition
t.Run(name, func(t *testing.T) {
// Skip tests that require specific Go version.
if name == "errors" {
ok := false
for _, version := range build.Default.ReleaseTags {
if version == "go1.16" {
ok = true
break
}
}
if !ok {
t.Skip("Results for errors test are only valid for Go 1.16+")
}
}
// Read the AST in memory.
path := filepath.Join("testdata", name+".go")
fset := token.NewFileSet()
@@ -55,7 +63,7 @@ func TestCGo(t *testing.T) {
}
// Process the AST with CGo.
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "")
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags, "")
// Check the AST for type errors.
var typecheckErrors []error
@@ -95,11 +103,11 @@ func TestCGo(t *testing.T) {
if err != nil {
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.
outfile := filepath.Join("testdata", name+".out.go")
expectedBytes, err := os.ReadFile(outfile)
expectedBytes, err := ioutil.ReadFile(outfile)
if err != nil {
t.Fatalf("could not read expected output: %v", err)
}
@@ -110,7 +118,7 @@ func TestCGo(t *testing.T) {
// It is not. Test failed.
if *flagUpdate {
// Update the file with the expected data.
err := os.WriteFile(outfile, []byte(actual), 0666)
err := ioutil.WriteFile(outfile, []byte(actual), 0666)
if err != nil {
t.Error("could not write updated output file:", err)
}
@@ -122,84 +130,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
// importing the unsafe package.
type simpleImporter struct {
+2 -25
View File
@@ -14,9 +14,6 @@ import (
var (
prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error)
precedences = map[token.Token]int{
token.OR: precedenceOr,
token.XOR: precedenceXor,
token.AND: precedenceAnd,
token.ADD: precedenceAdd,
token.SUB: precedenceAdd,
token.MUL: precedenceMul,
@@ -27,9 +24,6 @@ var (
const (
precedenceLowest = iota + 1
precedenceOr
precedenceXor
precedenceAnd
precedenceAdd
precedenceMul
precedencePrefix
@@ -82,7 +76,7 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
for t.peekToken != token.EOF && precedence < precedences[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()
leftExpr, err = parseBinaryExpr(t, leftExpr)
}
@@ -205,18 +199,7 @@ func (t *tokenizer) Next() {
// https://en.cppreference.com/w/cpp/string/byte/isspace
t.peekPos++
t.buf = t.buf[1:]
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&"):
// 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 == '^':
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%':
// Single-character tokens.
// TODO: ++ (increment) and -- (decrement) operators.
switch c {
@@ -234,12 +217,6 @@ func (t *tokenizer) Next() {
t.peekToken = token.QUO
case '%':
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.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`, `error: 1:2: unexpected token ||, expected end of expression`}, // logical binops aren't supported yet
{`(5/5)`, `(5 / 5)`},
{`1 - 2`, `1 - 2`},
{`1 - 2 + 3`, `1 - 2 + 3`},
+19 -70
View File
@@ -4,9 +4,7 @@ package cgo
// modification. It does not touch the AST itself.
import (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"go/ast"
"go/scanner"
@@ -15,13 +13,10 @@ import (
"strconv"
"strings"
"unsafe"
"tinygo.org/x/go-llvm"
)
/*
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
#include <llvm/Config/llvm-config.h>
#include <stdlib.h>
#include <stdint.h>
@@ -45,8 +40,6 @@ typedef struct {
GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu);
unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
CXString tinygo_clang_getCursorSpelling(GoCXCursor c);
CXString tinygo_clang_getCursorPrettyPrinted(GoCXCursor c, CXPrintingPolicy Policy);
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(GoCXCursor c);
enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c);
CXType tinygo_clang_getCursorType(GoCXCursor c);
GoCXCursor tinygo_clang_getTypeDeclaration(CXType t);
@@ -54,7 +47,6 @@ CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c);
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(GoCXCursor c);
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
@@ -84,16 +76,6 @@ var diagnosticSeverity = [...]string{
// theory decoupled from Clang) can also use this type.
type clangCursor = C.GoCXCursor
func init() {
// Check that we haven't messed up LLVM versioning.
// This can happen when llvm_config_*.go files in either this or the
// tinygo.org/x/go-llvm packages is incorrect. It should not ever happen
// with byollvm.
if C.LLVM_VERSION_STRING != llvm.Version {
panic("incorrect build: using LLVM version " + llvm.Version + " in the tinygo.org/x/llvm package, and version " + C.LLVM_VERSION_STRING + " in the ./cgo package")
}
}
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
index := C.clang_createIndex(0, 0)
defer C.clang_disposeIndex(index)
@@ -194,7 +176,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
// Convert the AST node under the given Clang cursor to a Go AST node and return
// it.
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
kind := C.tinygo_clang_getCursorKind(c)
pos := f.getCursorPosition(c)
switch kind {
@@ -205,43 +187,19 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
Kind: ast.Fun,
Name: "C." + name,
}
exportName := name
localName := name
var stringSignature string
if C.tinygo_clang_Cursor_getStorageClass(c) == C.CX_SC_Static {
// A static function is assigned a globally unique symbol name based
// on the file path (like _Cgo_static_2d09198adbf58f4f4655_foo) and
// has a different Go name in the form of C.foo!symbols.go instead
// of just C.foo.
path := f.importPath + "/" + filepath.Base(f.fset.File(f.file.Pos()).Name())
staticIDBuf := sha256.Sum256([]byte(path))
staticID := hex.EncodeToString(staticIDBuf[:10])
exportName = "_Cgo_static_" + staticID + "_" + name
localName = name + "!" + filepath.Base(path)
// Create a signature. This is necessary for MacOS to forward the
// call, because MacOS doesn't support aliases like ELF and PE do.
// (There is N_INDR but __attribute__((alias("..."))) doesn't work).
policy := C.tinygo_clang_getCursorPrintingPolicy(c)
defer C.clang_PrintingPolicy_dispose(policy)
C.clang_PrintingPolicy_setProperty(policy, C.CXPrintingPolicy_TerseOutput, 1)
stringSignature = getString(C.tinygo_clang_getCursorPrettyPrinted(c, policy))
stringSignature = strings.Replace(stringSignature, " "+name+"(", " "+exportName+"(", 1)
stringSignature = strings.TrimPrefix(stringSignature, "static ")
}
args := make([]*ast.Field, numArgs)
decl := &ast.FuncDecl{
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: pos - 1,
Text: "//export " + exportName,
Text: "//export " + name,
},
},
},
Name: &ast.Ident{
NamePos: pos,
Name: "C." + localName,
Name: "C." + name,
Obj: obj,
},
Type: &ast.FuncType{
@@ -292,7 +250,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
}
}
obj.Decl = decl
return decl, stringSignature
return decl, nil
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
typ := f.makeASTRecordType(c, pos)
typeName := "C." + name
@@ -533,26 +491,6 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
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.
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
@@ -808,7 +746,20 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
}
if name == "" {
// 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)
} else {
name = cgoRecordPrefix + name
@@ -821,9 +772,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
cursor := C.tinygo_clang_getTypeDeclaration(typ)
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
if name == "" {
// Anonymous enum, probably inside a typedef.
location := f.getUniqueLocationID(pos, cursor)
name = f.getUnnamedDeclName("_Ctype_enum___", location)
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
} else {
name = "enum_" + name
}
+16
View File
@@ -0,0 +1,16 @@
//go:build !byollvm && !llvm14
// +build !byollvm,!llvm14
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-13/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@13/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@13/include
#cgo freebsd CFLAGS: -I/usr/local/llvm13/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-13/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@13/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@13/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm13/lib -lclang
*/
import "C"
+1
View File
@@ -1,4 +1,5 @@
//go:build !byollvm && llvm14
// +build !byollvm,llvm14
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"
-12
View File
@@ -17,14 +17,6 @@ CXString tinygo_clang_getCursorSpelling(CXCursor c) {
return clang_getCursorSpelling(c);
}
CXString tinygo_clang_getCursorPrettyPrinted(CXCursor c, CXPrintingPolicy policy) {
return clang_getCursorPrettyPrinted(c, policy);
}
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(CXCursor c) {
return clang_getCursorPrintingPolicy(c);
}
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
return clang_getCursorKind(c);
}
@@ -53,10 +45,6 @@ CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
return clang_Cursor_getArgument(c, i);
}
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(CXCursor c) {
return clang_Cursor_getStorageClass(c);
}
CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
return clang_getCursorLocation(c);
}
+2 -2
View File
@@ -8,9 +8,9 @@
// 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: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:112: undefined: C.SOME_CONST_4
// testdata/errors.go:112: undeclared name: C.SOME_CONST_4
package main
-2
View File
@@ -5,7 +5,6 @@ package main
int foo(int a, int b);
void variadic0();
void variadic2(int x, int y, ...);
static void staticfunc(int x);
// Global variable signatures.
extern int someValue;
@@ -17,7 +16,6 @@ func accessFunctions() {
C.foo(3, 4)
C.variadic0()
C.variadic2(3, 5)
C.staticfunc(3)
}
func accessGlobals() {
-5
View File
@@ -55,10 +55,5 @@ func C.variadic2(x C.int, y C.int)
var C.variadic2$funcaddr unsafe.Pointer
//export _Cgo_static_173c95a79b6df1980521_staticfunc
func C.staticfunc!symbols.go(x C.int)
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
//go:extern someValue
var C.someValue C.int
-4
View File
@@ -112,10 +112,6 @@ import "C"
import "C"
var (
// aliases
_ C.float
_ C.double
// Simple typedefs.
_ C.myint
+33 -72
View File
@@ -47,12 +47,6 @@ func (c *Config) Features() string {
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:
// for example, bare-metal targets will usually pretend to be linux to get the
// standard library to compile.
@@ -75,12 +69,13 @@ func (c *Config) GOARM() string {
// BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string {
targetTags := filterTags(c.Target.BuildTags, c.Options.Tags)
tags := append(targetTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
for i := 1; i <= c.GoMinorVersion; i++ {
tags = append(tags, fmt.Sprintf("go1.%d", i))
}
tags = append(tags, c.Options.Tags...)
if extraTags := strings.Fields(c.Options.Tags); len(extraTags) != 0 {
tags = append(tags, extraTags...)
}
return tags
}
@@ -91,7 +86,7 @@ func (c *Config) CgoEnabled() bool {
}
// 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 {
if c.Options.GC != "" {
return c.Options.GC
@@ -106,7 +101,7 @@ func (c *Config) GC() string {
// that can be traced by the garbage collector.
func (c *Config) NeedsStackObjects() bool {
switch c.GC() {
case "conservative", "custom", "precise":
case "conservative":
for _, tag := range c.BuildTags() {
if tag == "tinygo.wasm" {
return true
@@ -165,14 +160,6 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
}
}
// LTO returns one of the possible LTO configurations: legacy or thin.
func (c *Config) LTO() string {
if c.Options.LTO != "" {
return c.Options.LTO
}
return "legacy"
}
// PanicStrategy returns the panic strategy selected for this target. Valid
// values are "print" (print the panic value, then exit) or "trap" (issue a trap
// instruction).
@@ -190,13 +177,23 @@ func (c *Config) AutomaticStackSize() bool {
return false
}
// StackSize returns the default stack size to be used for goroutines, if the
// stack size could not be determined automatically at compile time.
func (c *Config) StackSize() uint64 {
if c.Options.StackSize != 0 {
return c.Options.StackSize
// 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 {
parts := strings.Split(c.Triple(), "-")
if parts[0] == "wasm32" {
// wasm-ld doesn't seem to support ThinLTO yet.
return false
}
return c.Target.DefaultStackSize
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
}
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
@@ -226,9 +223,6 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
if c.CPU() != "" {
archname += "-" + c.CPU()
}
if c.ABI() != "" {
archname += "-" + c.ABI()
}
// Try to load a precompiled library.
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
@@ -319,7 +313,7 @@ func (c *Config) CFlags() []string {
panic("unknown libc: " + c.Target.Libc)
}
// 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.
cflags = append(cflags, "-O"+c.Options.Opt)
// Set the LLVM target triple.
@@ -337,10 +331,6 @@ func (c *Config) CFlags() []string {
cflags = append(cflags, "-mcpu="+c.Target.CPU)
}
}
// Set the -mabi flag, if needed.
if c.ABI() != "" {
cflags = append(cflags, "-mabi="+c.ABI())
}
return cflags
}
@@ -380,8 +370,8 @@ func (c *Config) VerifyIR() bool {
}
// Debug returns whether debug (DWARF) information should be retained by the
// linker. By default, debug information is retained, but it can be removed
// with the -no-debug flag.
// linker. By default, debug information is retained but it can be removed with
// the -no-debug flag.
func (c *Config) Debug() bool {
return c.Options.Debug
}
@@ -452,13 +442,13 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
if openocdInterface == "" {
return nil, errors.New("OpenOCD programmer not set")
}
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(openocdInterface) {
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(openocdInterface) {
return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", openocdInterface)
}
if c.Target.OpenOCDTarget == "" {
return nil, errors.New("OpenOCD chip not set")
}
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(c.Target.OpenOCDTarget) {
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(c.Target.OpenOCDTarget) {
return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", c.Target.OpenOCDTarget)
}
if c.Target.OpenOCDTransport != "" && c.Target.OpenOCDTransport != "swd" {
@@ -469,14 +459,7 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
args = append(args, "-c", cmd)
}
if c.Target.OpenOCDTransport != "" {
transport := c.Target.OpenOCDTransport
if transport == "swd" {
switch openocdInterface {
case "stlink-dap":
transport = "dapdirect_swd"
}
}
args = append(args, "-c", "transport select "+transport)
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
}
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
return args, nil
@@ -501,8 +484,12 @@ func (c *Config) RelocationModel() string {
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 {
if c.Options.WasmAbi != "" {
return c.Options.WasmAbi
}
return c.Target.WasmAbi
}
@@ -539,8 +526,6 @@ func (c *Config) Emulator(format, binary string) ([]string, error) {
var emulator []string
for _, s := range parts {
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)
emulator = append(emulator, s)
}
@@ -551,27 +536,3 @@ type TestConfig struct {
CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc
}
// filterTags removes predefined build tags for a target if a conflicting option
// is provided by the user.
func filterTags(targetTags []string, userTags []string) []string {
var filtered []string
for _, t := range targetTags {
switch {
case strings.HasPrefix(t, "runtime_memhash_"):
overridden := false
for _, ut := range userTags {
if strings.HasPrefix(ut, "runtime_memhash_") {
overridden = true
break
}
}
if !overridden {
filtered = append(filtered, t)
}
default:
filtered = append(filtered, t)
}
}
return filtered
}
-132
View File
@@ -1,132 +0,0 @@
package compileopts
import (
"fmt"
"strings"
"testing"
)
func TestBuildTags(t *testing.T) {
tests := []struct {
targetTags []string
userTags []string
result []string
}{
{
targetTags: []string{},
userTags: []string{},
result: []string{
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
},
},
{
targetTags: []string{"bear"},
userTags: []string{},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
},
},
{
targetTags: []string{},
userTags: []string{"cat"},
result: []string{
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
},
},
{
targetTags: []string{"bear"},
userTags: []string{"cat"},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
},
},
{
targetTags: []string{"bear", "runtime_memhash_leveldb"},
userTags: []string{"cat"},
result: []string{
"bear",
"runtime_memhash_leveldb",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
},
},
{
targetTags: []string{"bear", "runtime_memhash_leveldb"},
userTags: []string{"cat", "runtime_memhash_leveldb"},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
"runtime_memhash_leveldb",
},
},
{
targetTags: []string{"bear", "runtime_memhash_leveldb"},
userTags: []string{"cat", "runtime_memhash_sip"},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
"runtime_memhash_sip",
},
},
}
for _, tc := range tests {
tt := tc
t.Run(fmt.Sprintf("%s+%s", strings.Join(tt.targetTags, ","), strings.Join(tt.userTags, ",")), func(t *testing.T) {
c := &Config{
Target: &TargetSpec{
BuildTags: tt.targetTags,
},
Options: &Options{
Tags: tt.userTags,
},
}
res := c.BuildTags()
if len(res) != len(tt.result) {
t.Errorf("expected %d tags, got %d", len(tt.result), len(res))
}
for i, tag := range tt.result {
if tag != res[i] {
t.Errorf("tag %d: expected %s, got %s", i, tt.result[i], tag)
}
}
})
}
}
+3 -16
View File
@@ -4,17 +4,15 @@ import (
"fmt"
"regexp"
"strings"
"time"
)
var (
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
validGCOptions = []string{"none", "leaking", "conservative"}
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb"}
validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"}
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
validLTOOptions = []string{"legacy", "thin"}
)
// Options contains extra options to give to the compiler. These options are
@@ -26,14 +24,11 @@ type Options struct {
GOARM string // environment variable (only used with GOARCH=arm)
Target string
Opt string
LTO string
GC string
PanicStrategy string
Scheduler string
StackSize uint64 // goroutine stack size (if none could be automatically determined)
Serial string
Work bool // -work flag to print temporary build directory
InterpTimeout time.Duration
PrintIR bool
DumpSSA bool
VerifyIR bool
@@ -43,7 +38,8 @@ type Options struct {
PrintSizes string
PrintAllocs *regexp.Regexp // regexp string
PrintStacks bool
Tags []string
Tags string
WasmAbi string
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
TestConfig TestConfig
Programmer string
@@ -51,9 +47,6 @@ type Options struct {
LLVMFeatures string
Directory string
PrintJSON bool
Monitor bool
BaudRate int
Timeout time.Duration
}
// Verify performs a validation on the given options, raising an error if options are not valid.
@@ -109,12 +102,6 @@ func (o *Options) Verify() error {
}
}
if o.LTO != "" {
if !isInArray(validLTOOptions, o.LTO) {
return fmt.Errorf("invalid -lto=%s: valid values are %s", o.LTO, strings.Join(validLTOOptions, ", "))
}
}
return nil
}
+1 -7
View File
@@ -9,7 +9,7 @@ import (
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`)
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`)
@@ -48,12 +48,6 @@ func TestVerifyOptions(t *testing.T) {
GC: "conservative",
},
},
{
name: "GCOptionCustom",
opts: compileopts.Options{
GC: "custom",
},
},
{
name: "InvalidSchedulerOption",
opts: compileopts.Options{
+21 -50
View File
@@ -26,7 +26,6 @@ type TargetSpec struct {
Inherits []string `json:"inherits"`
Triple string `json:"llvm-target"`
CPU string `json:"cpu"`
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag
Features string `json:"features"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
@@ -48,7 +47,7 @@ type TargetSpec struct {
FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"`
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"`
FlashVolume string `json:"msd-volume-name"`
FlashFilename string `json:"msd-firmware-name"`
@@ -66,7 +65,7 @@ type TargetSpec struct {
}
// overrideProperties overrides all properties that are set in child into itself using reflection.
func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
specType := reflect.TypeOf(spec).Elem()
specValue := reflect.ValueOf(spec).Elem()
childValue := reflect.ValueOf(child).Elem()
@@ -89,22 +88,12 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
if !src.IsNil() {
dst.Set(src)
}
case reflect.Slice: // for slices, append the field and check for duplicates
case reflect.Slice: // for slices, append the field
dst.Set(reflect.AppendSlice(dst, src))
for i := 0; i < dst.Len(); i++ {
v := dst.Index(i).String()
for j := i + 1; j < dst.Len(); j++ {
w := dst.Index(j).String()
if v == w {
return fmt.Errorf("duplicate value '%s' in field %s", v, field.Name)
}
}
}
default:
return fmt.Errorf("unknown field type: %s", kind)
panic("unknown field type : " + kind.String())
}
}
return nil
}
// load reads a target specification from the JSON in the given io.Reader. It
@@ -119,10 +108,10 @@ func (spec *TargetSpec) load(r io.Reader) error {
}
// loadFromGivenStr loads the TargetSpec from the given string that could be:
// - targets/ directory inside the compiler sources
// - a relative or absolute path to custom (project specific) target specification .json file;
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
// as well as path to custom files (ex. myAwesomeProject.json)
// - targets/ directory inside the compiler sources
// - a relative or absolute path to custom (project specific) target specification .json file;
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
// as well as path to custom files (ex. myAwesomeProject.json)
func (spec *TargetSpec) loadFromGivenStr(str string) error {
path := ""
if strings.HasSuffix(str, ".json") {
@@ -152,17 +141,11 @@ func (spec *TargetSpec) resolveInherits() error {
if err != nil {
return err
}
err = newSpec.overrideProperties(subtarget)
if err != nil {
return err
}
newSpec.overrideProperties(subtarget)
}
// When all properties are loaded, make sure they are properly inherited.
err := newSpec.overrideProperties(spec)
if err != nil {
return err
}
newSpec.overrideProperties(spec)
*spec = *newSpec
return nil
@@ -195,7 +178,6 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
default:
llvmarch = options.GOARCH
}
llvmvendor := "unknown"
llvmos := options.GOOS
if llvmos == "darwin" {
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
@@ -205,18 +187,17 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
// Looks like Apple prefers to call this architecture ARM64
// instead of AArch64.
llvmarch = "arm64"
llvmos = "macosx11.0.0"
}
llvmvendor = "apple"
}
// Target triples (which actually have four components, but are called
// triples for historical reasons) have the form:
// arch-vendor-os-environment
target := llvmarch + "-" + llvmvendor + "-" + llvmos
target := llvmarch + "-unknown-" + llvmos
if options.GOARCH == "arm" {
target += "-gnueabihf"
}
if options.GOOS == "windows" {
target += "-gnu"
} else if options.GOARCH == "arm" {
target += "-gnueabihf"
}
return defaultTarget(options.GOOS, options.GOARCH, target)
}
@@ -232,7 +213,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
// it includes all parents as specified in the "inherits" key.
err = spec.resolveInherits()
if err != nil {
return nil, fmt.Errorf("%s : %w", options.Target, err)
return nil, err
}
if spec.Scheduler == "asyncify" {
@@ -250,7 +231,6 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
GOOS: goos,
GOARCH: goarch,
BuildTags: []string{goos, goarch},
GC: "precise",
Scheduler: "tasks",
Linker: "cc",
DefaultStackSize: 1024 * 64, // 64kB
@@ -304,19 +284,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// normally present in Go (without explicitly opting in).
// For more discussion:
// 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,
"-m", "i386pep",
"-Bdynamic",
"--image-base", "0x400000",
"--gc-sections",
"--no-insert-timestamp",
"--no-dynamicbase",
@@ -326,12 +297,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
}
if goarch != "wasm" {
suffix := ""
if goos == "windows" && goarch == "amd64" {
// Windows uses a different calling convention on amd64 from other
// operating systems so we need separate assembly files.
if goos == "windows" {
// Windows uses a different calling convention from other operating
// systems so we need separate assembly files.
suffix = "_windows"
}
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
}
if goarch != runtime.GOARCH {
+2 -3
View File
@@ -1,8 +1,7 @@
package compileopts
import (
"errors"
"io/fs"
"os"
"reflect"
"testing"
)
@@ -18,7 +17,7 @@ func TestLoadTarget(t *testing.T) {
t.Error("LoadTarget should have failed with non existing target")
}
if !errors.Is(err, fs.ErrNotExist) {
if !os.IsNotExist(err) {
t.Error("LoadTarget failed for wrong reason:", err)
}
}
+46 -7
View File
@@ -16,8 +16,6 @@ import "tinygo.org/x/go-llvm"
var stdlibAliases = map[string]string{
// crypto packages
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
"crypto/ed25519/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
"crypto/md5.block": "crypto/md5.blockGeneric",
"crypto/sha1.block": "crypto/sha1.blockGeneric",
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
@@ -25,18 +23,59 @@ var stdlibAliases = map[string]string{
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
// math package
"math.Asin": "math.asin",
"math.Asinh": "math.asinh",
"math.Acos": "math.acos",
"math.Acosh": "math.acosh",
"math.Atan": "math.atan",
"math.Atanh": "math.atanh",
"math.Atan2": "math.atan2",
"math.Cbrt": "math.cbrt",
"math.Ceil": "math.ceil",
"math.archCeil": "math.ceil",
"math.Cos": "math.cos",
"math.Cosh": "math.cosh",
"math.Erf": "math.erf",
"math.Erfc": "math.erfc",
"math.Exp": "math.exp",
"math.archExp": "math.exp",
"math.Expm1": "math.expm1",
"math.Exp2": "math.exp2",
"math.archExp2": "math.exp2",
"math.Floor": "math.floor",
"math.archFloor": "math.floor",
"math.Frexp": "math.frexp",
"math.Hypot": "math.hypot",
"math.archHypot": "math.hypot",
"math.Ldexp": "math.ldexp",
"math.Log": "math.log",
"math.archLog": "math.log",
"math.Log1p": "math.log1p",
"math.Log10": "math.log10",
"math.Log2": "math.log2",
"math.Max": "math.max",
"math.archMax": "math.max",
"math.Min": "math.min",
"math.archMin": "math.min",
"math.Mod": "math.mod",
"math.Modf": "math.modf",
"math.archModf": "math.modf",
"math.Pow": "math.pow",
"math.Remainder": "math.remainder",
"math.Sin": "math.sin",
"math.Sinh": "math.sinh",
"math.Sqrt": "math.sqrt",
"math.archSqrt": "math.sqrt",
"math.Tan": "math.tan",
"math.Tanh": "math.tanh",
"math.Trunc": "math.trunc",
"math.archTrunc": "math.trunc",
}
// createAlias implements the function (in the builder) as a call to the alias
// function.
func (b *builder) createAlias(alias llvm.Value) {
if !b.LTO {
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
}
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
b.llvmFn.SetUnnamedAddr(true)
if b.Debug {
@@ -47,14 +86,14 @@ func (b *builder) createAlias(alias llvm.Value) {
pos := b.program.Fset.Position(b.fn.Pos())
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
}
entryBlock := b.ctx.AddBasicBlock(b.llvmFn, "entry")
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
b.SetInsertPointAtEnd(entryBlock)
if b.llvmFn.Type() != alias.Type() {
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
b.CreateUnreachable()
return
}
result := b.CreateCall(alias.GlobalValueType(), alias, b.llvmFn.Params(), "")
result := b.CreateCall(alias, b.llvmFn.Params(), "")
if result.Type().TypeKind() == llvm.VoidTypeKind {
b.CreateRetVoid()
} else {
+8 -11
View File
@@ -89,11 +89,10 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
}
// createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice
// and unsafe.String. This function must panic if the ptr/len parameters are
// invalid.
func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value, elementType llvm.Type, lenType *types.Basic) {
// From the documentation of unsafe.Slice and unsafe.String:
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
// function must panic if the ptr/len parameters are invalid.
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
// From the documentation of unsafe.Slice:
// > At run time, if len is negative, or if ptr is nil and len is not
// > zero, a run-time panic occurs.
// 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
// len parameter.
maxSize := b.maxSliceSize(elementType)
maxSize := b.maxSliceSize(ptr.Type().ElementType())
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
// 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, "")
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
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
@@ -146,7 +145,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
}
// Make the maxBufSize actually the maximum allowed value (in number of
// 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.
if maxBufSize.Type() != bufSize.Type() {
@@ -241,10 +240,8 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
}
}
// Put the fault block at the end of the function and the next block at the
// current insert position.
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next")
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now branch to the out-of-bounds or the regular block.
+26 -27
View File
@@ -4,17 +4,19 @@ import (
"fmt"
"strings"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic
// operation. It returns the result of the operation, or a zero llvm.Value if
// the result is void.
func (b *builder) createAtomicOp(name string) llvm.Value {
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
// operation. It returns the result of the operation and true if the call could
// be lowered inline, and false otherwise.
func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
name := call.Value.(*ssa.Function).Name()
switch name {
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
ptr := b.getValue(b.fn.Params[0])
val := b.getValue(b.fn.Params[1])
ptr := b.getValue(call.Args[0])
val := b.getValue(call.Args[1])
if strings.HasPrefix(b.Triple, "avr") {
// AtomicRMW does not work on AVR as intended:
// - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release)
@@ -25,20 +27,19 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
if fn.IsNil() {
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 b.CreateAdd(oldVal, val, "")
return b.CreateAdd(oldVal, val, ""), true
}
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
// Return the new value, not the original value returned by atomicrmw.
return b.CreateAdd(oldVal, val, "")
return b.CreateAdd(oldVal, val, ""), true
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
ptr := b.getValue(b.fn.Params[0])
val := b.getValue(b.fn.Params[1])
ptr := b.getValue(call.Args[0])
val := b.getValue(call.Args[1])
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
if isPointer {
// atomicrmw only supports integers, so cast to an integer.
// TODO: this is fixed in LLVM 15.
val = b.CreatePtrToInt(val, b.uintptrType, "")
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
}
@@ -46,23 +47,23 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
if isPointer {
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
}
return oldVal
return oldVal, true
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
ptr := b.getValue(b.fn.Params[0])
old := b.getValue(b.fn.Params[1])
newVal := b.getValue(b.fn.Params[2])
ptr := b.getValue(call.Args[0])
old := b.getValue(call.Args[1])
newVal := b.getValue(call.Args[2])
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
swapped := b.CreateExtractValue(tuple, 1, "")
return swapped
return swapped, true
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
ptr := b.getValue(b.fn.Params[0])
val := b.CreateLoad(b.getLLVMType(b.fn.Signature.Results().At(0).Type()), ptr, "")
ptr := b.getValue(call.Args[0])
val := b.CreateLoad(ptr, "")
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
return val
return val, true
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
ptr := b.getValue(b.fn.Params[0])
val := b.getValue(b.fn.Params[1])
ptr := b.getValue(call.Args[0])
val := b.getValue(call.Args[1])
if strings.HasPrefix(b.Triple, "avr") {
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
vType := val.Type()
@@ -78,15 +79,13 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
if fn.IsNil() {
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)}, "")
return llvm.Value{}
return b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, ""), true
}
store := b.CreateStore(val, ptr)
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
return llvm.Value{}
return store, true
default:
b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name())
return llvm.Value{}
return llvm.Value{}, false
}
}
+40 -58
View File
@@ -20,7 +20,7 @@ const maxFieldsPerParam = 3
type paramInfo struct {
llvmType llvm.Type
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
@@ -33,54 +33,26 @@ const (
paramIsDeferenceableOrNull = 1 << iota
)
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
// createRuntimeInvoke instead.
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
// createCall creates a new call to runtime.<fnName> with the given arguments.
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
fnType, llvmFn := b.getFunction(fn)
llvmFn := b.getFunction(fn)
if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil))
}
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
if isInvoke {
return b.createInvoke(fnType, llvmFn, args, name)
}
return b.createCall(fnType, llvmFn, args, name)
}
// createRuntimeCall creates a new call to runtime.<fnName> with the given
// arguments.
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
return b.createRuntimeCallCommon(fnName, args, name, false)
}
// createRuntimeInvoke creates a new call to runtime.<fnName> with the given
// arguments. If the runtime call panics, control flow is diverted to the
// landing pad block.
// Note that "invoke" here is meant in the LLVM sense (a call that can
// panic/throw), not in the Go sense (an interface method call).
func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name string) llvm.Value {
return b.createRuntimeCallCommon(fnName, args, name, true)
return b.createCall(llvmFn, args, name)
}
// createCall creates a call to the given function with the arguments possibly
// 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))
for _, arg := range args {
fragments := b.expandFormalParam(arg)
expanded = append(expanded, fragments...)
}
return b.CreateCall(fnType, fn, expanded, name)
}
// createInvoke is like createCall but continues execution at the landing pad if
// the call resulted in a panic.
func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
if b.hasDeferFrame() {
b.createInvokeCheckpoint()
}
return b.createCall(fnType, fn, args, name)
return b.CreateCall(fn, expanded, name)
}
// Expand an argument type to a list that can be used in a function call
@@ -96,7 +68,13 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
// failed to expand this parameter: too many fields
}
// 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
@@ -146,6 +124,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
// with the passed in type if this is not possible.
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
typeFlags := getTypeFlags(goType)
switch t.TypeKind() {
case llvm.StructTypeKind:
var paramInfos []paramInfo
@@ -176,37 +155,40 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
}
}
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
for i := range subInfos {
subInfos[i].flags |= typeFlags
}
paramInfos = append(paramInfos, subInfos...)
}
return paramInfos
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
// parameter is pointer-like, it will also store the element type for the
// dereferenceable_or_null attribute.
func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Type) paramInfo {
info := paramInfo{
llvmType: t,
name: name,
// getTypeFlags returns the type flags for a given type. It will not recurse
// into sub-types (such as in structs).
func getTypeFlags(t types.Type) paramFlags {
if t == nil {
return 0
}
if goType != nil {
switch underlying := goType.Underlying().(type) {
case *types.Pointer:
// Pointers in Go must either point to an object or be nil.
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMType(underlying.Elem()))
case *types.Chan:
// Channels are implemented simply as a *runtime.channel.
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("channel"))
case *types.Map:
// Maps are similar to channels: they are implemented as a
// *runtime.hashmap.
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("hashmap"))
}
switch t.Underlying().(type) {
case *types.Pointer:
// Pointers in Go must either point to an object or be nil.
return paramIsDeferenceableOrNull
case *types.Chan, *types.Map:
// Channels and maps are implemented as pointers pointing to some
// object, and follow the same rules as *types.Pointer.
return paramIsDeferenceableOrNull
default:
return 0
}
return info
}
// 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.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the send.
@@ -75,7 +75,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
}
// Allocate blockedlist buffer.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the receive.
@@ -84,7 +84,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
if isZeroSize {
received = llvm.ConstNull(valueType)
} else {
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
received = b.CreateLoad(valueAlloca, "chan.received")
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
}
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))
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
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),
}, "select.recvbuf")
@@ -184,13 +184,13 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
for i, state := range selectStates {
// 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(), uint64(i), false),
}, "")
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),
}, "select.states")
@@ -204,7 +204,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
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),
}, "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
// it to the correct type, and dereference it.
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
typ := b.getLLVMType(expr.Type())
ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "")
return b.CreateLoad(typ, ptr, "")
typ := llvm.PointerType(b.getLLVMType(expr.Type()), 0)
ptr := b.CreateBitCast(recvbuf, typ, "")
return b.CreateLoad(ptr, "")
}
}
+152 -321
View File
@@ -18,7 +18,6 @@ import (
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
"tinygo.org/x/go-llvm"
)
@@ -41,13 +40,11 @@ type Config struct {
Triple string
CPU string
Features string
ABI string
GOOS string
GOARCH string
CodeModel string
RelocationModel string
SizeLevel int
TinyGoVersion string // for llvm.ident
// Various compiler options that determine how code is generated.
Scheduler string
@@ -55,7 +52,6 @@ type Config struct {
DefaultStackSize uint64
NeedsStackObjects bool
Debug bool // Whether to emit debug information in the LLVM module.
LTO bool // non-legacy LTO (meaning: package bitcode is merged by the linker)
}
// compilerContext contains function-independent data that should still be
@@ -66,19 +62,17 @@ type compilerContext struct {
DumpSSA bool
mod llvm.Module
ctx llvm.Context
builder llvm.Builder // only used for constant operations
dibuilder *llvm.DIBuilder
cu llvm.Metadata
difiles map[string]llvm.Metadata
ditypes map[types.Type]llvm.Metadata
llvmTypes typeutil.Map
llvmTypes map[types.Type]llvm.Type
machine llvm.TargetMachine
targetData llvm.TargetData
intType llvm.Type
i8ptrType llvm.Type // for convenience
rawVoidFuncType llvm.Type // for convenience
funcPtrAddrSpace int
hasTypedPointers bool // for LLVM 14 backwards compatibility
uintptrType llvm.Type
program *ssa.Program
diagnostics []error
@@ -97,13 +91,13 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
DumpSSA: dumpSSA,
difiles: make(map[string]llvm.Metadata),
ditypes: make(map[types.Type]llvm.Metadata),
llvmTypes: make(map[types.Type]llvm.Type),
machine: machine,
targetData: machine.CreateTargetData(),
astComments: map[string]*ast.CommentGroup{},
}
c.ctx = llvm.NewContext()
c.builder = c.ctx.NewBuilder()
c.mod = c.ctx.NewModule(moduleName)
c.mod.SetTarget(config.Triple)
c.mod.SetDataLayout(c.targetData.String())
@@ -126,25 +120,17 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
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()
dummyFunc.EraseFromParentAsFunction()
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.
type builder struct {
*compilerContext
llvm.Builder
fn *ssa.Function
llvmFnType llvm.Type
llvmFn llvm.Value
info functionInfo
locals map[ssa.Value]llvm.Value // local variables
@@ -153,9 +139,6 @@ type builder struct {
currentBlock *ssa.BasicBlock
phis []phiNode
deferPtr llvm.Value
deferFrame llvm.Value
stackChainAlloca llvm.Value
landingpad llvm.BasicBlock
difunc llvm.Metadata
dilocals map[*types.Var]llvm.Metadata
initInlinedAt llvm.Metadata // fake inlinedAt position
@@ -170,13 +153,11 @@ type builder struct {
}
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
fnType, fn := c.getFunction(f)
return &builder{
compilerContext: c,
Builder: irbuilder,
fn: f,
llvmFnType: fnType,
llvmFn: fn,
llvmFn: c.getFunction(f),
info: c.getFunctionInfo(f),
locals: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata),
@@ -270,7 +251,6 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
// 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) {
c := newCompilerContext(moduleName, machine, config, dumpSSA)
defer c.dispose()
c.packageDir = pkg.OriginalDir()
c.embedGlobals = pkg.EmbedGlobals
c.pkg = pkg.Pkg
@@ -323,42 +303,19 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
}),
)
if c.TinyGoVersion != "" {
// It is necessary to set llvm.ident, otherwise debugging on MacOS
// won't work.
c.mod.AddNamedMetadataOperand("llvm.ident",
c.ctx.MDNode(([]llvm.Metadata{
c.ctx.MDString("TinyGo version " + c.TinyGoVersion),
})))
}
c.dibuilder.Finalize()
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
}
func (c *compilerContext) getRuntimeType(name string) types.Type {
return c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
}
// getLLVMRuntimeType obtains a named type from the runtime package and returns
// it as a LLVM type, creating it if necessary. It is a shorthand for
// getLLVMType(getRuntimeType(name)).
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
return c.getLLVMType(c.getRuntimeType(name))
typ := c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
return c.getLLVMType(typ)
}
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
@@ -366,16 +323,12 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
// important for named struct types (which should only be created once).
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
// Try to load the LLVM type from the cache.
// Note: *types.Named isn't unique when working with generics.
// See https://github.com/golang/go/issues/53914
// This is the reason for using typeutil.Map to lookup LLVM types for Go types.
ival := c.llvmTypes.At(goType)
if ival != nil {
return ival.(llvm.Type)
if t, ok := c.llvmTypes[goType]; ok {
return t
}
// Not already created, so adding this type to the cache.
llvmType := c.makeLLVMType(goType)
c.llvmTypes.Set(goType, llvmType)
c.llvmTypes[goType] = llvmType
return llvmType
}
@@ -429,29 +382,23 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
// in LLVM IR, named structs are implemented as named structs in
// LLVM. This is because it is otherwise impossible to create
// self-referencing types such as linked lists.
llvmName := typ.String()
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
llvmType := c.ctx.StructCreateNamed(llvmName)
c.llvmTypes.Set(goType, llvmType) // avoid infinite recursion
c.llvmTypes[goType] = llvmType // avoid infinite recursion
underlying := c.getLLVMType(st)
llvmType.StructSetBody(underlying.StructElementTypes(), false)
return llvmType
}
return c.getLLVMType(typ.Underlying())
case *types.Pointer:
if c.hasTypedPointers {
ptrTo := c.getLLVMType(typ.Elem())
return llvm.PointerType(ptrTo, 0)
}
return c.i8ptrType // all pointers are the same
ptrTo := c.getLLVMType(typ.Elem())
return llvm.PointerType(ptrTo, 0)
case *types.Signature: // function value
return c.getFuncType(typ)
case *types.Slice:
ptrType := c.i8ptrType
if c.hasTypedPointers {
ptrType = llvm.PointerType(c.getLLVMType(typ.Elem()), 0)
}
elemType := c.getLLVMType(typ.Elem())
members := []llvm.Type{
ptrType,
llvm.PointerType(elemType, 0),
c.uintptrType, // len
c.uintptrType, // cap
}
@@ -462,8 +409,6 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
members[i] = c.getLLVMType(typ.Field(i).Type())
}
return c.ctx.StructType(members, false)
case *types.TypeParam:
return c.getLLVMType(typ.Underlying())
case *types.Tuple:
members := make([]llvm.Type, typ.Len())
for i := 0; i < typ.Len(); i++ {
@@ -662,8 +607,6 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
})
temporaryMDNode.ReplaceAllUsesWith(md)
return md
case *types.TypeParam:
return c.getDIType(typ.Underlying())
default:
panic("unknown type while generating DWARF debug type: " + typ.String())
}
@@ -733,7 +676,7 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
Name: param.Name(),
File: b.getDIFile(pos.Filename),
Line: pos.Line,
Type: b.getDIType(param.Type()),
Type: b.getDIType(variable.Type()),
AlwaysPreserve: true,
ArgNo: i + 1,
})
@@ -758,8 +701,7 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
// DISubprogram metadata node.
func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
pos := c.program.Fset.Position(f.Syntax().Pos())
_, fn := c.getFunction(f)
return c.attachDebugInfoRaw(f, fn, "", pos.Filename, pos.Line)
return c.attachDebugInfoRaw(f, c.getFunction(f), "", pos.Filename, pos.Line)
}
// attachDebugInfo adds debug info to a function declaration. It returns the
@@ -832,25 +774,10 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
if member.TypeParams() != nil {
// Do not try to build generic (non-instantiated) functions.
continue
}
// Create the function definition.
b := newBuilder(c, irbuilder, member)
if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
// The body of this function (if there is one) is ignored and
// replaced with a LLVM intrinsic call.
b.defineMathOp()
continue
}
if member.Blocks == nil {
// Try to define this as an intrinsic function.
b.defineIntrinsicFunction()
// It might not be an intrinsic function but simply an external
// function (defined via //go:linkname). Leave it undefined in
// that case.
continue
continue // external function
}
b.createFunction()
case *ssa.Type:
@@ -867,9 +794,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
for _, method := range methods {
// Parse this method.
fn := pkg.Prog.MethodValue(method)
if fn == nil {
continue // probably a generic method
}
if fn.Blocks == nil {
continue // external function
}
@@ -897,10 +821,8 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
if files, ok := c.embedGlobals[member.Name()]; ok {
c.createEmbedGlobal(member, global, files)
} else if !info.extern {
global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
if !c.LTO {
global.SetVisibility(llvm.HiddenVisibility)
}
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
global.SetVisibility(llvm.HiddenVisibility)
if info.section != "" {
global.SetSection(info.section)
}
@@ -947,9 +869,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
}
strObj := c.getEmbedFileString(files[0])
global.SetInitializer(strObj)
if !c.LTO {
global.SetVisibility(llvm.HiddenVisibility)
}
global.SetVisibility(llvm.HiddenVisibility)
case *types.Slice:
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
@@ -966,16 +886,14 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
bufferGlobal.SetInitializer(bufferValue)
bufferGlobal.SetLinkage(llvm.InternalLinkage)
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),
})
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
global.SetInitializer(sliceObj)
if !c.LTO {
global.SetVisibility(llvm.HiddenVisibility)
}
global.SetVisibility(llvm.HiddenVisibility)
case *types.Struct:
// Assume this is an embed.FS struct:
@@ -1022,10 +940,10 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
for _, file := range allFiles {
fileStruct := llvm.ConstNull(embedFileStructType)
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 != "" {
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)
}
@@ -1040,7 +958,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
// Create the slice object itself.
// Because embed.FS refers to it as *[]embed.file instead of a plain
// []embed.file, we have to store this as a global.
slicePtr := llvm.ConstInBoundsGEP(sliceDataInitializer.Type(), sliceDataGlobal, []llvm.Value{
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false),
})
@@ -1056,11 +974,9 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
// Define the embed.FS struct. It has only one field: the files (as a
// *[]embed.file).
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
global.SetInitializer(globalInitializer)
if !c.LTO {
global.SetVisibility(llvm.HiddenVisibility)
}
global.SetVisibility(llvm.HiddenVisibility)
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
}
}
@@ -1070,11 +986,11 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
dataGlobalName := "embed/file_" + file.Hash
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
if dataGlobal.IsNil() {
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
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),
})
@@ -1082,11 +998,10 @@ func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
}
// Start defining a function so that it can be filled with instructions: load
// parameters, create basic blocks, and set up debug information.
// This is separated out from createFunction() so that it is also usable to
// define compiler intrinsics like the atomic operations in sync/atomic.
func (b *builder) createFunctionStart(intrinsic bool) {
// createFunction builds the LLVM IR implementation for this function. The
// function must not yet be defined, otherwise this function will create a
// diagnostic.
func (b *builder) createFunction() {
if b.DumpSSA {
fmt.Printf("\nfunc %s:\n", b.fn)
}
@@ -1099,18 +1014,9 @@ func (b *builder) createFunctionStart(intrinsic bool) {
b.addError(b.fn.Pos(), errValue)
return
}
b.addStandardDefinedAttributes(b.llvmFn)
if !b.info.exported {
// Do not set visibility for local linkage (internal or private).
// Otherwise a "local linkage requires default visibility"
// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
// is thrown.
if b.llvmFn.Linkage() != llvm.InternalLinkage &&
b.llvmFn.Linkage() != llvm.PrivateLinkage &&
!b.LTO {
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
}
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
b.llvmFn.SetUnnamedAddr(true)
}
if b.info.section != "" {
@@ -1121,11 +1027,6 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// otherwise the function is not exported.
functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)
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.
@@ -1165,21 +1066,12 @@ func (b *builder) createFunctionStart(intrinsic bool) {
}
// Pre-create all basic blocks in the function.
var entryBlock llvm.BasicBlock
if intrinsic {
// This function isn't defined in Go SSA. It is probably a compiler
// intrinsic (like an atomic operation). Create the entry block
// manually.
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
} else {
for _, block := range b.fn.DomPreorder() {
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
b.blockEntries[block] = llvmBlock
b.blockExits[block] = llvmBlock
}
// Normal functions have an entry block.
entryBlock = b.blockEntries[b.fn.Blocks[0]]
for _, block := range b.fn.DomPreorder() {
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
b.blockEntries[block] = llvmBlock
b.blockExits[block] = llvmBlock
}
entryBlock := b.blockEntries[b.fn.Blocks[0]]
b.SetInsertPointAtEnd(entryBlock)
if b.fn.Synthetic == "package initializer" {
@@ -1255,20 +1147,6 @@ func (b *builder) createFunctionStart(intrinsic bool) {
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
// function must not yet be defined, otherwise this function will create a
// diagnostic.
func (b *builder) createFunction() {
b.createFunctionStart(false)
// Fill blocks with instructions.
for _, block := range b.fn.DomPreorder() {
if b.DumpSSA {
@@ -1315,12 +1193,6 @@ func (b *builder) createFunction() {
}
}
if b.hasDeferFrame() {
// Create the landing pad block, where execution continues after a
// panic.
b.createLandingPad()
}
// Resolve phi nodes
for _, phi := range b.phis {
block := phi.ssa.Block()
@@ -1346,7 +1218,6 @@ func (b *builder) createFunction() {
// Create anonymous functions (closures etc.).
for _, sub := range b.fn.AnonFuncs {
b := newBuilder(b.compilerContext, b.Builder, sub)
b.llvmFn.SetLinkage(llvm.InternalLinkage)
b.createFunction()
}
}
@@ -1449,19 +1320,16 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
case *ssa.Panic:
value := b.getValue(instr.X)
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
b.createRuntimeCall("_panic", []llvm.Value{value}, "")
b.CreateUnreachable()
case *ssa.Return:
if b.hasDeferFrame() {
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
}
if len(instr.Results) == 0 {
b.CreateRetVoid()
} else if len(instr.Results) == 1 {
b.CreateRet(b.getValue(instr.Results[0]))
} else {
// 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 {
val := b.getValue(result)
retVal = b.CreateInsertValue(retVal, val, i, "")
@@ -1500,7 +1368,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr")
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)
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new")
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
@@ -1553,7 +1421,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
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")
srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr")
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
@@ -1631,12 +1499,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
case *types.Pointer:
ptrValue := b.CreatePtrToInt(value, b.uintptrType, "")
b.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "")
case *types.Slice:
bufptr := b.CreateExtractValue(value, 0, "")
buflen := b.CreateExtractValue(value, 1, "")
bufcap := b.CreateExtractValue(value, 2, "")
ptrValue := b.CreatePtrToInt(bufptr, b.uintptrType, "")
b.createRuntimeCall("printslice", []llvm.Value{ptrValue, buflen, bufcap}, "")
default:
return llvm.Value{}, b.makeError(pos, "unknown arg type: "+typ.String())
}
@@ -1649,13 +1511,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
cplx := argValues[0]
return b.CreateExtractValue(cplx, 0, "real"), nil
case "recover":
useParentFrame := uint64(0)
if b.hasDeferFrame() {
// recover() should return the panic value of the parent function,
// not of the current function.
useParentFrame = 1
}
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
return b.createRuntimeCall("_recover", nil, ""), nil
case "ssa:wrapnilchk":
// TODO: do an actual nil check?
return argValues[0], nil
@@ -1666,35 +1522,20 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
// Note: the pointer is always of type *i8.
ptr := argValues[0]
len := argValues[1]
return b.CreateGEP(b.ctx.Int8Type(), ptr, []llvm.Value{len}, ""), nil
case "Alignof": // unsafe.Alignof
align := b.targetData.ABITypeAlignment(argValues[0].Type())
return llvm.ConstInt(b.uintptrType, uint64(align), false), nil
case "Offsetof": // unsafe.Offsetof
// This builtin is a bit harder to implement and may need a bit of
// refactoring to work (it may be easier to implement if we have access
// to the underlying Go SSA instruction). It is also rarely used: it
// only applies in generic code and unsafe.Offsetof isn't very commonly
// used anyway.
// In other words, postpone it to some other day.
return llvm.Value{}, b.makeError(pos, "todo: unsafe.Offsetof")
case "Sizeof": // unsafe.Sizeof
size := b.targetData.TypeAllocSize(argValues[0].Type())
return llvm.ConstInt(b.uintptrType, size, false), nil
case "Slice", "String": // unsafe.Slice, unsafe.String
// This creates a slice or string from a pointer and a length.
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
case "Slice": // unsafe.Slice
// This creates a slice from a pointer and a length.
// Note that the exception mentioned in the documentation (if the
// pointer and length are nil, the slice is also nil) is trivially
// already the case.
ptr := argValues[0]
len := argValues[1]
var elementType llvm.Type
if callName == "Slice" {
elementType = b.getLLVMType(argTypes[0].Underlying().(*types.Pointer).Elem())
} else {
elementType = b.ctx.Int8Type()
}
b.createUnsafeSliceStringCheck("unsafe."+callName, ptr, len, elementType, argTypes[1].Underlying().(*types.Basic))
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
ptr.Type(),
b.uintptrType,
b.uintptrType,
}, false))
b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
// Too small, zero-extend len.
len = b.CreateZExt(len, b.uintptrType, "")
@@ -1702,24 +1543,10 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
// Too big, truncate len.
len = b.CreateTrunc(len, b.uintptrType, "")
}
if callName == "Slice" {
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
ptr.Type(),
b.uintptrType,
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
slice = b.CreateInsertValue(slice, ptr, 0, "")
slice = b.CreateInsertValue(slice, len, 1, "")
slice = b.CreateInsertValue(slice, len, 2, "")
return slice, nil
default:
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
}
@@ -1738,13 +1565,21 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
// Try to call the function directly for trivially static calls.
var callee, context llvm.Value
var calleeType llvm.Type
exported := false
if fn := instr.StaticCallee(); fn != nil {
// Direct function call, either to a named or anonymous (directly
// applied) function call. If it is anonymous, it may be a closure.
name := fn.RelString(nil)
switch {
case name == "runtime.memcpy" || name == "runtime.memmove" || name == "reflect.memcpy":
return b.createMemoryCopyCall(fn, instr.Args)
case name == "runtime.memzero":
return b.createMemoryZeroCall(instr.Args)
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
result, ok := b.createMathOp(instr)
if ok {
return result, nil
}
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
return b.createInlineAsm(instr.Args)
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
@@ -1759,17 +1594,23 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.createSyscall(instr)
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
return b.createRawSyscallNoError(instr)
case name == "runtime.supportsRecover":
supportsRecover := uint64(0)
if b.supportsRecover() {
supportsRecover = 1
case strings.HasPrefix(name, "runtime/volatile.Load"):
return b.createVolatileLoad(instr)
case strings.HasPrefix(name, "runtime/volatile.Store"):
return b.createVolatileStore(instr)
case strings.HasPrefix(name, "sync/atomic."):
val, ok := b.createAtomicOp(instr)
if ok {
// This call could be lowered as an atomic operation.
return val, nil
}
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
// This call couldn't be lowered as an atomic operation, it's
// probably something else. Continue as usual.
case name == "runtime/interrupt.New":
return b.createInterruptGlobal(instr)
}
calleeType, callee = b.getFunction(fn)
callee = b.getFunction(fn)
info := b.getFunctionInfo(fn)
if callee.IsNil() {
return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+info.linkName)
@@ -1783,8 +1624,8 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
// Eventually we might be able to eliminate this special case
// entirely. For details, see:
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(calleeType, b.funcPtrAddrSpace))
fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
}
case *ssa.MakeClosure:
// A call on a func value, but the callee is trivial to find. For
@@ -1811,14 +1652,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
params = append([]llvm.Value{value}, params...)
params = append(params, typecode)
callee = b.getInvokeFunction(instr)
calleeType = callee.GlobalValueType()
context = llvm.Undef(b.i8ptrType)
} else {
// Function pointer.
value := b.getValue(instr.Value)
// This is a func value, which cannot be called directly. We have to
// 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")
}
@@ -1828,7 +1668,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
params = append(params, context)
}
return b.createInvoke(calleeType, callee, params, ""), nil
return b.createCall(callee, params, ""), nil
}
// getValue returns the LLVM value of a constant, function value, global, or
@@ -1842,8 +1682,7 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String())
return llvm.Undef(b.getLLVMType(expr.Type()))
}
_, fn := b.getFunction(expr)
return b.createFuncValue(fn, llvm.Undef(b.i8ptrType), expr.Signature)
return b.createFuncValue(b.getFunction(expr), llvm.Undef(b.i8ptrType), expr.Signature)
case *ssa.Global:
value := b.getGlobal(expr)
if value.IsNil() {
@@ -1991,77 +1830,46 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(expr.Field), false),
}
elementType := b.getLLVMType(expr.X.Type().Underlying().(*types.Pointer).Elem())
return b.CreateInBoundsGEP(elementType, val, indices, ""), nil
return b.CreateInBoundsGEP(val, indices, ""), nil
case *ssa.Function:
panic("function is not an expression")
case *ssa.Global:
panic("global is not an expression")
case *ssa.Index:
collection := b.getValue(expr.X)
array := b.getValue(expr.X)
index := b.getValue(expr.Index)
switch xType := expr.X.Type().Underlying().(type) {
case *types.Basic: // extract byte from string
// Value type must be a string, which is a basic type.
if xType.Info()&types.IsString == 0 {
panic("lookup on non-string?")
}
// Extend index to at least uintptr size, because getelementptr assumes
// index is a signed integer.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
// 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)
// Check bounds.
arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
b.createLookupBoundsCheck(arrayLenLLVM, index)
// Bounds check.
length := b.CreateExtractValue(collection, 1, "len")
b.createLookupBoundsCheck(length, index)
// Lookup byte
buf := b.CreateExtractValue(collection, 0, "")
bufElemType := b.ctx.Int8Type()
bufPtr := b.CreateInBoundsGEP(bufElemType, buf, []llvm.Value{index}, "")
return b.CreateLoad(bufElemType, bufPtr, ""), 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")
}
// Can't load directly from array (as index is non-constant), so have to
// do it using an alloca+gep+load.
alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(array.Type(), "index.alloca")
b.CreateStore(array, alloca)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
ptr := b.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep")
result := b.CreateLoad(ptr, "index.load")
b.emitLifetimeEnd(allocaPtr, allocaSize)
return result, nil
case *ssa.IndexAddr:
val := b.getValue(expr.X)
index := b.getValue(expr.Index)
// Get buffer pointer and length
var bufptr, buflen llvm.Value
var bufType llvm.Type
switch ptrTyp := expr.X.Type().Underlying().(type) {
case *types.Pointer:
typ := ptrTyp.Elem().Underlying()
typ := expr.X.Type().Underlying().(*types.Pointer).Elem().Underlying()
switch typ := typ.(type) {
case *types.Array:
bufptr = val
buflen = llvm.ConstInt(b.uintptrType, uint64(typ.Len()), false)
bufType = b.getLLVMType(typ)
// Check for nil pointer before calculating the address, from
// the spec:
// > For an operand x of type T, the address operation &x
@@ -2075,7 +1883,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
case *types.Slice:
bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr")
buflen = b.CreateExtractValue(val, 1, "indexaddr.len")
bufType = b.getLLVMType(ptrTyp.Elem())
default:
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
}
@@ -2093,20 +1900,47 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
index,
}
return b.CreateInBoundsGEP(bufType, bufptr, indices, ""), nil
return b.CreateInBoundsGEP(bufptr, indices, ""), nil
case *types.Slice:
return b.CreateInBoundsGEP(bufType, bufptr, []llvm.Value{index}, ""), nil
return b.CreateInBoundsGEP(bufptr, []llvm.Value{index}, ""), nil
default:
panic("unreachable")
}
case *ssa.Lookup: // map lookup
case *ssa.Lookup:
value := b.getValue(expr.X)
index := b.getValue(expr.Index)
valueType := expr.Type()
if expr.CommaOk {
valueType = valueType.(*types.Tuple).At(0).Type()
switch xType := expr.X.Type().Underlying().(type) {
case *types.Basic:
// 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:
return b.createMakeChan(expr), nil
case *ssa.MakeClosure:
@@ -2230,8 +2064,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
switch typ := expr.X.Type().Underlying().(type) {
case *types.Pointer: // pointer to array
// slice an array
arrayType := typ.Elem().Underlying().(*types.Array)
length := arrayType.Len()
length := typ.Elem().Underlying().(*types.Array).Len()
llvmLen := llvm.ConstInt(b.uintptrType, uint64(length), false)
if high.IsNil() {
high = llvmLen
@@ -2260,7 +2093,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
}
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")
slice := b.ctx.ConstStruct([]llvm.Value{
@@ -2299,8 +2132,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
max = b.CreateTrunc(max, b.uintptrType, "")
}
ptrElemType := b.getLLVMType(typ.Elem())
newPtr := b.CreateInBoundsGEP(ptrElemType, oldPtr, []llvm.Value{low}, "")
newPtr := b.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
newLen := b.CreateSub(high, low, "")
newCap := b.CreateSub(max, low, "")
slice := b.ctx.ConstStruct([]llvm.Value{
@@ -2340,7 +2172,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
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, "")
str := llvm.Undef(b.getLLVMRuntimeType("_string"))
str = b.CreateInsertValue(str, newPtr, 0, "")
@@ -2803,15 +2635,14 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
var strPtr llvm.Value
if str != "" {
objname := c.pkg.Path() + "$string"
globalType := llvm.ArrayType(c.ctx.Int8Type(), len(str))
global := llvm.AddGlobal(c.mod, globalType, objname)
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
global.SetInitializer(c.ctx.ConstString(str, false))
global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetAlignment(1)
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
} else {
strPtr = llvm.ConstNull(c.i8ptrType)
}
@@ -2836,15 +2667,15 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
return cplx
} else if typ.Kind() == types.Complex128 {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
return cplx
} else {
panic("unknown constant of basic type: " + expr.String())
@@ -2931,7 +2762,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p
// create a GEP that is not in bounds. However, we're
// talking about unsafe code here so the programmer has to
// be careful anyway.
return b.CreateInBoundsGEP(b.ctx.Int8Type(), origptr, []llvm.Value{index}, ""), nil
return b.CreateInBoundsGEP(origptr, []llvm.Value{index}, ""), nil
}
}
}
@@ -3164,10 +2995,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())
}
case token.MUL: // *x, dereference pointer
valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Pointer).Elem())
if b.targetData.TypeAllocSize(valueType) == 0 {
unop.X.Type().Underlying().(*types.Pointer).Elem()
if b.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
// zero-length data
return llvm.ConstNull(valueType), nil
return llvm.ConstNull(x.Type().ElementType()), nil
} else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
// CGo function pointer. The cgo part has rewritten CGo function
// pointers as stub global variables of the form:
@@ -3175,14 +3006,14 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
// Instead of a load from the global, create a bitcast of the
// function pointer itself.
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() {
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
}
return b.CreateBitCast(fn, b.i8ptrType, ""), nil
} else {
b.createNilCheck(unop.X, x, "deref")
load := b.CreateLoad(valueType, x, "")
load := b.CreateLoad(x, "")
return load, nil
}
case token.XOR: // ^x, toggle all bits in integer
+19 -11
View File
@@ -3,7 +3,7 @@ package compiler
import (
"flag"
"go/types"
"os"
"io/ioutil"
"strconv"
"strings"
"testing"
@@ -28,6 +28,12 @@ type testCase struct {
func TestCompiler(t *testing.T) {
t.Parallel()
// Determine LLVM version.
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil {
t.Fatal("could not parse LLVM version:", llvm.Version)
}
// Determine Go minor version (e.g. 16 in go1.16.3).
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
if err != nil {
@@ -43,16 +49,20 @@ func TestCompiler(t *testing.T) {
{"float.go", "", ""},
{"interface.go", "", ""},
{"func.go", "", ""},
{"defer.go", "cortex-m-qemu", ""},
{"pragma.go", "", ""},
{"goroutine.go", "wasm", "asyncify"},
{"goroutine.go", "cortex-m-qemu", "tasks"},
{"channel.go", "", ""},
{"intrinsics.go", "cortex-m-qemu", ""},
{"intrinsics.go", "wasm", ""},
{"gc.go", "", ""},
{"zeromap.go", "", ""},
}
if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""})
if llvmMajor >= 12 {
tests = append(tests, testCase{"intrinsics.go", "cortex-m-qemu", ""})
tests = append(tests, testCase{"intrinsics.go", "wasm", ""})
}
if goMinor >= 17 {
tests = append(tests, testCase{"go1.17.go", "", ""})
}
for _, tc := range tests {
@@ -83,15 +93,13 @@ func TestCompiler(t *testing.T) {
}
compilerConfig := &Config{
Triple: config.Triple(),
Features: config.Features(),
ABI: config.ABI(),
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(),
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.StackSize(),
DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(),
}
machine, err := NewTargetMachine(compilerConfig)
@@ -101,7 +109,7 @@ func TestCompiler(t *testing.T) {
defer machine.Dispose()
// Load entire program AST into memory.
lprogram, err := loader.Load(config, "./testdata/"+tc.file, config.ClangHeaders, types.Config{
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
Sizes: Sizes(machine),
})
if err != nil {
@@ -149,14 +157,14 @@ func TestCompiler(t *testing.T) {
// Update test if needed. Do not check the result.
if *flagUpdate {
err := os.WriteFile(outPath, []byte(mod.String()), 0666)
err := ioutil.WriteFile(outPath, []byte(mod.String()), 0666)
if err != nil {
t.Error("failed to write updated output file:", err)
}
return
}
expected, err := os.ReadFile(outPath)
expected, err := ioutil.ReadFile(outPath)
if err != nil {
t.Fatal("failed to read golden file:", err)
}
+44 -225
View File
@@ -15,39 +15,12 @@ package compiler
import (
"go/types"
"strconv"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// supportsRecover returns whether the compiler supports the recover() builtin
// for the current architecture.
func (b *builder) supportsRecover() bool {
switch b.archFamily() {
case "wasm32":
// Probably needs to be implemented using the exception handling
// proposal of WebAssembly:
// https://github.com/WebAssembly/exception-handling
return false
case "riscv64", "xtensa":
// TODO: add support for these architectures
return false
default:
return true
}
}
// hasDeferFrame returns whether the current function needs to catch panics and
// run defers.
func (b *builder) hasDeferFrame() bool {
if b.fn.Recover == nil {
return false
}
return b.supportsRecover()
}
// deferInitFunc sets up this function for future deferred calls. It must be
// called from within the entry block when this function contains deferred
// calls.
@@ -63,151 +36,6 @@ func (b *builder) deferInitFunc() {
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
if b.hasDeferFrame() {
// Set up the defer frame with the current stack pointer.
// This assumes that the stack pointer doesn't move outside of the
// function prologue/epilogue (an invariant maintained by TinyGo but
// possibly broken by the C alloca function).
// The frame pointer is _not_ saved, because it is marked as clobbered
// in the setjmp-like inline assembly.
deferFrameType := b.getLLVMRuntimeType("deferFrame")
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
stackPointer := b.readStackPointer()
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
// Create the landing pad block, which is where control transfers after
// a panic.
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
}
}
// createLandingPad fills in the landing pad block. This block runs the deferred
// functions and returns (by jumping to the recover block). If the function is
// still panicking after the defers are run, the panic will be re-raised in
// destroyDeferFrame.
func (b *builder) createLandingPad() {
b.SetInsertPointAtEnd(b.landingpad)
// Add debug info, if needed.
// The location used is the closing bracket of the function.
if b.Debug {
pos := b.program.Fset.Position(b.fn.Syntax().End())
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
}
b.createRunDefers()
// Continue at the 'recover' block, which returns to the parent in an
// appropriate way.
b.CreateBr(b.blockEntries[b.fn.Recover])
}
// createInvokeCheckpoint saves the function state at the given point, to
// continue at the landing pad if a panic happened. This is implemented using a
// setjmp-like construct.
func (b *builder) createInvokeCheckpoint() {
// Construct inline assembly equivalents of setjmp.
// The assembly works as follows:
// * All registers (both callee-saved and caller saved) are clobbered
// after the inline assembly returns.
// * The assembly stores the address just past the end of the assembly
// into the jump buffer.
// * The return value (eax, rax, r0, etc) is set to zero in the inline
// assembly but set to an unspecified non-zero value when jumping using
// a longjmp.
var asmString, constraints string
resultType := b.uintptrType
switch b.archFamily() {
case "i386":
asmString = `
xorl %eax, %eax
movl $$1f, 4(%ebx)
1:`
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
// This doesn't include the floating point stack because TinyGo uses
// newer floating point instructions.
case "x86_64":
asmString = `
leaq 1f(%rip), %rax
movq %rax, 8(%rbx)
xorq %rax, %rax
1:`
constraints = "={rax},{rbx},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
// This list doesn't include AVX/AVX512 registers because TinyGo
// doesn't currently enable support for AVX instructions.
case "arm":
// Note: the following assembly takes into account that the PC is
// always 4 bytes ahead on ARM. The PC that is stored always points
// to the instruction just after the assembly fragment so that
// tinygo_longjmp lands at the correct instruction.
if b.isThumb() {
// Instructions are 2 bytes in size.
asmString = `
movs r0, #0
mov r2, pc
str r2, [r1, #4]`
} else {
// Instructions are 4 bytes in size.
asmString = `
str pc, [r1, #4]
movs r0, #0`
}
constraints = "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"
case "aarch64":
asmString = `
adr x2, 1f
str x2, [x1, #8]
mov x0, #0
1:
`
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{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" {
// These registers cause the following warning when compiling for
// MacOS and Windows:
// warning: inline asm clobber list contains reserved registers:
// X18, FP
// Reserved registers on the clobber list may not be preserved
// across the asm statement, and clobbering them may lead to
// undefined behaviour.
constraints += ",~{x18},~{fp}"
}
// TODO: SVE registers, which we don't use in TinyGo at the moment.
case "avr":
// Note: the Y register (R28:R29) is a fixed register and therefore
// needs to be saved manually. TODO: do this only once per function with
// a defer frame, not for every call.
resultType = b.ctx.Int8Type()
asmString = `
ldi r24, pm_lo8(1f)
ldi r25, pm_hi8(1f)
std z+2, r24
std z+3, r25
std z+4, r28
std z+5, r29
ldi r24, 0
1:`
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
case "riscv32":
asmString = `
la a2, 1f
sw a2, 4(a1)
li a0, 0
1:`
constraints = "={a0},{a1},~{a1},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{s0},~{s1},~{s2},~{s3},~{s4},~{s5},~{s6},~{s7},~{s8},~{s9},~{s10},~{s11},~{t0},~{t1},~{t2},~{t3},~{t4},~{t5},~{t6},~{ra},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{memory}"
default:
// This case should have been handled by b.supportsRecover().
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
}
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
continueBB := b.insertBasicBlock("")
b.CreateCondBr(isZero, continueBB, b.landingpad)
b.SetInsertPointAtEnd(continueBB)
b.blockExits[b.currentBlock] = continueBB
}
// isInLoop checks if there is a path from a basic block to itself.
@@ -249,8 +77,7 @@ func isInLoop(start *ssa.BasicBlock) bool {
func (b *builder) createDefer(instr *ssa.Defer) {
// The pointer to the previous defer struct, which we will replace to
// make a linked list.
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
next := b.CreateLoad(deferType, b.deferPtr, "defer.next")
next := b.CreateLoad(b.deferPtr, "defer.next")
var values []llvm.Value
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
@@ -271,7 +98,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
values = []llvm.Value{callback, next, typecode, receiverValue}
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
for _, arg := range instr.Call.Args {
val := b.getValue(arg)
values = append(values, val)
@@ -374,31 +201,30 @@ func (b *builder) createDefer(instr *ssa.Defer) {
}
}
// Make a struct out of the collected values to put in the deferred call
// struct.
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCall := llvm.ConstNull(deferredCallType)
// Make a struct out of the collected values to put in the defer frame.
deferFrameType := b.ctx.StructType(valueTypes, false)
deferFrame := llvm.ConstNull(deferFrameType)
for i, value := range values {
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
deferFrame = b.CreateInsertValue(deferFrame, value, i, "")
}
// Put this struct in an allocation.
var alloca llvm.Value
if !isInLoop(instr.Block()) {
// This can safely use a stack allocation.
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca")
} else {
// This may be hit a variable number of times, so use a heap allocation.
size := b.targetData.TypeAllocSize(deferredCallType)
size := b.targetData.TypeAllocSize(deferFrameType)
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
nilPtr := llvm.ConstNull(b.i8ptrType)
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferredCallType, 0), "defer.alloc")
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
}
if b.NeedsStackObjects {
b.trackPointer(alloca)
}
b.CreateStore(deferredCall, alloca)
b.CreateStore(deferFrame, alloca)
// Push it on top of the linked list by replacing deferPtr.
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
@@ -407,9 +233,6 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// createRunDefers emits code to run all deferred functions.
func (b *builder) createRunDefers() {
deferType := b.getLLVMRuntimeType("_defer")
deferPtrType := llvm.PointerType(deferType, 0)
// Add a loop like the following:
// for stack != nil {
// _stack := stack
@@ -425,17 +248,17 @@ func (b *builder) createRunDefers() {
// }
// }
// Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end.
end := b.insertBasicBlock("rundefers.end")
unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default")
loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop")
loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead")
// Create loop.
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
b.CreateBr(loophead)
// Create loop head:
// for stack != nil {
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")
b.CreateCondBr(stackIsNil, end, loop)
@@ -444,24 +267,24 @@ func (b *builder) createRunDefers() {
// stack = stack.next
// switch stack.callback {
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(), 1, false), // .next field
}, "stack.next.gep")
nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next")
nextStack := b.CreateLoad(nextStackGEP, "stack.next")
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), // .callback field
}, "callback.gep")
callback := b.CreateLoad(b.uintptrType, gep, "callback")
callback := b.CreateLoad(gep, "callback")
sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs))
for i, callback := range b.allDeferFuncs {
// Create switch case, for example:
// case 0:
// // run first deferred call
block := b.insertBasicBlock("rundefers.callback" + strconv.Itoa(i))
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
b.SetInsertPointAtEnd(block)
switch callback := callback.(type) {
@@ -472,31 +295,30 @@ func (b *builder) createRunDefers() {
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
if !callback.IsInvoke() {
//Expect funcValue to be passed through the deferred call.
//Expect funcValue to be passed through the defer frame.
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
} else {
//Expect typecode
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
}
for _, arg := range callback.Args {
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
}
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
deferFrameType := b.ctx.StructType(valueTypes, false)
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// Extract the params from the struct (including receiver).
forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
forwardParam := b.CreateLoad(gep, "param")
forwardParams = append(forwardParams, forwardParam)
}
var fnPtr llvm.Value
var fnType llvm.Type
if !callback.IsInvoke() {
// Isolate the func value.
@@ -504,8 +326,8 @@ func (b *builder) createRunDefers() {
forwardParams = forwardParams[1:]
//Get function pointer and context
var context llvm.Value
fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature())
fp, context := b.decodeFuncValue(funcValue, callback.Signature())
fnPtr = fp
//Pass context
forwardParams = append(forwardParams, context)
@@ -514,7 +336,6 @@ func (b *builder) createRunDefers() {
// parameters.
forwardParams = append(forwardParams[1:], forwardParams[0])
fnPtr = b.getInvokeFunction(callback)
fnType = fnPtr.GlobalValueType()
// Add the context parameter. An interface call cannot also be a
// closure but we have to supply the parameter anyway for platforms
@@ -522,7 +343,7 @@ func (b *builder) createRunDefers() {
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
}
b.createCall(fnType, fnPtr, forwardParams, "")
b.createCall(fnPtr, forwardParams, "")
case *ssa.Function:
// Direct call.
@@ -532,15 +353,15 @@ func (b *builder) createRunDefers() {
for _, param := range getParams(callback.Signature) {
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
}
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
deferFrameType := b.ctx.StructType(valueTypes, false)
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// Extract the params from the struct.
forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := range getParams(callback.Signature) {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(gep, "param")
forwardParams = append(forwardParams, forwardParam)
}
@@ -553,8 +374,7 @@ func (b *builder) createRunDefers() {
}
// Call real function.
fnType, fn := b.getFunction(callback)
b.createInvoke(fnType, fn, forwardParams, "")
b.createCall(b.getFunction(callback), forwardParams, "")
case *ssa.MakeClosure:
// Get the real defer struct type and cast to it.
@@ -565,21 +385,20 @@ func (b *builder) createRunDefers() {
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
}
valueTypes = append(valueTypes, b.i8ptrType) // closure
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
deferFrameType := b.ctx.StructType(valueTypes, false)
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// Extract the params from the struct.
forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
forwardParam := b.CreateLoad(gep, "param")
forwardParams = append(forwardParams, forwardParam)
}
// Call deferred function.
fnType, llvmFn := b.getFunction(fn)
b.createCall(fnType, llvmFn, forwardParams, "")
b.createCall(b.getFunction(fn), forwardParams, "")
case *ssa.Builtin:
db := b.deferBuiltinFuncs[callback]
@@ -592,15 +411,15 @@ func (b *builder) createRunDefers() {
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
}
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
deferFrameType := b.ctx.StructType(valueTypes, false)
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// Extract the params from the struct.
var argValues []llvm.Value
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < params.Len(); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(gep, "param")
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
// 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, "")
funcPtr = b.CreateExtractValue(funcValue, 1, "")
if !funcPtr.IsAConstantExpr().IsNil() && funcPtr.Opcode() == llvm.BitCast {
funcPtr = funcPtr.Operand(0) // needed for LLVM 14 (no opaque pointers)
}
if sig != nil {
funcType = b.getRawFuncType(sig)
llvmSig := llvm.PointerType(funcType, b.funcPtrAddrSpace)
funcPtr = b.CreateBitCast(funcPtr, llvmSig, "")
bitcast := b.CreateExtractValue(funcValue, 1, "")
if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
funcPtr = bitcast.Operand(0)
return
}
llvmSig := b.getRawFuncType(sig)
funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
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)
}
// 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 {
// Get the return type.
var returnType llvm.Type
@@ -119,7 +117,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
paramTypes = append(paramTypes, c.i8ptrType) // context
// 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
@@ -143,6 +141,5 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
context := b.emitPointerPack(boundVars)
// Create the closure.
_, fn := b.getFunction(f)
return b.createFuncValue(fn, context, f.Signature), nil
return b.createFuncValue(b.getFunction(f), context, f.Signature), nil
}
+1 -1
View File
@@ -84,7 +84,7 @@ func (b *builder) trackPointer(value llvm.Value) {
if value.Type() != 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.
+34 -39
View File
@@ -7,6 +7,7 @@ import (
"go/token"
"go/types"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -21,7 +22,6 @@ func (b *builder) createGo(instr *ssa.Go) {
var prefix string
var funcPtr llvm.Value
var funcPtrType llvm.Type
hasContext := false
if callee := instr.Call.StaticCallee(); callee != nil {
// 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
hasContext = true
}
funcPtrType, funcPtr = b.getFunction(callee)
funcPtr = b.getFunction(callee)
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
// We cheat. None of the builtins do any long or blocking operation, so
// we might as well run these builtins right away without the program
@@ -80,7 +80,6 @@ func (b *builder) createGo(instr *ssa.Go) {
itfTypeCode := b.CreateExtractValue(itf, 0, "")
itfValue := b.CreateExtractValue(itf, 1, "")
funcPtr = b.getInvokeFunction(&instr.Call)
funcPtrType = funcPtr.GlobalValueType()
params = append([]llvm.Value{itfValue}, params...) // start with receiver
params = append(params, itfTypeCode) // end with typecode
} else {
@@ -90,7 +89,7 @@ func (b *builder) createGo(instr *ssa.Go) {
// * The function context, for closures.
// * The function pointer (for tasks).
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)
hasContext = true
prefix = b.fn.RelString(nil)
@@ -98,14 +97,14 @@ func (b *builder) createGo(instr *ssa.Go) {
paramBundle := b.emitPointerPack(params)
var stackSize llvm.Value
callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos())
callee := b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF
// section that contains the stack size (and is modified after
// linking).
stackSizeFnType, 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")
stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
} else {
// The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized.
@@ -114,23 +113,23 @@ func (b *builder) createGo(instr *ssa.Go) {
}
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
}
fnType, 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)}, "")
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "")
}
// createGoroutineStartWrapper creates a wrapper for the task-based
// implementation of goroutines. For example, to call a function like this:
//
// func add(x, y int) int { ... }
// func add(x, y int) int { ... }
//
// It creates a wrapper like this:
//
// func add$gowrapper(ptr *unsafe.Pointer) {
// args := (*struct{
// x, y int
// })(ptr)
// add(args.x, args.y)
// }
// func add$gowrapper(ptr *unsafe.Pointer) {
// args := (*struct{
// x, y int
// })(ptr)
// add(args.x, args.y)
// }
//
// This is useful because the task-based goroutine start implementation only
// allows a single (pointer) argument to the newly started goroutine. Also, it
@@ -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
// false, the parameter bundle is assumed to have no context parameter and undef
// 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
b := &builder{
compilerContext: c,
Builder: c.ctx.NewBuilder(),
}
defer b.Dispose()
builder := c.ctx.NewBuilder()
defer builder.Dispose()
var deadlock llvm.Value
var deadlockType llvm.Type
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() {
@@ -172,7 +167,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
wrapper.SetUnnamedAddr(true)
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
entry := c.ctx.AddBasicBlock(wrapper, "entry")
b.SetInsertPointAtEnd(entry)
builder.SetInsertPointAtEnd(entry)
if c.Debug {
pos := c.program.Fset.Position(pos)
@@ -193,24 +188,24 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
Optimized: true,
})
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.
paramTypes := fnType.ParamTypes()
paramTypes := fn.Type().ElementType().ParamTypes()
if !hasContext {
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 {
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
}
// Create the call.
b.CreateCall(fnType, fn, params, "")
builder.CreateCall(fn, params, "")
if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{
builder.CreateCall(deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType),
}, "")
}
@@ -241,7 +236,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
wrapper.SetUnnamedAddr(true)
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
entry := c.ctx.AddBasicBlock(wrapper, "entry")
b.SetInsertPointAtEnd(entry)
builder.SetInsertPointAtEnd(entry)
if c.Debug {
pos := c.program.Fset.Position(pos)
@@ -262,23 +257,23 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
Optimized: true,
})
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.
paramTypes := fnType.ParamTypes()
paramTypes := fn.Type().ElementType().ParamTypes()
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.
fnPtr := params[len(params)-1]
params = params[:len(params)-1]
// Create the call.
b.CreateCall(fnType, fnPtr, params, "")
builder.CreateCall(fnPtr, params, "")
if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{
builder.CreateCall(deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType),
}, "")
}
@@ -286,13 +281,13 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if c.Scheduler == "asyncify" {
// The goroutine was terminated via deadlock.
b.CreateUnreachable()
builder.CreateUnreachable()
} else {
// Finish the function. Every basic block must end in a terminator, and
// 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 b.CreatePtrToInt(wrapper, c.uintptrType, "")
return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
}
+30 -30
View File
@@ -17,7 +17,7 @@ import (
// operands or return values. It is useful for trivial instructions, like wfi in
// ARM or sleep in AVR.
//
// func Asm(asm string)
// func Asm(asm string)
//
// The provided assembly must be a constant.
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
@@ -25,23 +25,23 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
asm := constant.StringVal(args[0].(*ssa.Const).Value)
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
// way.
//
// func AsmFull(asm string, regs map[string]interface{}) uintptr
// func AsmFull(asm string, regs map[string]interface{}) uintptr
//
// The asm parameter must be a constant string. The regs parameter must be
// provided immediately. For example:
//
// arm.AsmFull(
// "str {value}, {result}",
// map[string]interface{}{
// "value": 1
// "result": &dest,
// })
// arm.AsmFull(
// "str {value}, {result}",
// map[string]interface{}{
// "value": 1
// "result": &dest,
// })
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
registers := map[string]llvm.Value{}
@@ -120,7 +120,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
}
fnType := llvm.FunctionType(outputType, argTypes, 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 {
return result, nil
} else {
@@ -132,11 +132,11 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
// This is a compiler builtin which emits an inline SVCall instruction. It can
// be one of:
//
// func SVCall0(num uintptr) uintptr
// func SVCall1(num uintptr, a1 interface{}) uintptr
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
// func SVCall0(num uintptr) uintptr
// func SVCall1(num uintptr, a1 interface{}) uintptr
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
//
// The num parameter must be a constant. All other parameters may be any scalar
// value supported by LLVM inline assembly.
@@ -163,17 +163,17 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
constraints += ",~{r1},~{r2},~{r3}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, 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
// be one of:
//
// func SVCall0(num uintptr) uintptr
// func SVCall1(num uintptr, a1 interface{}) uintptr
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
// func SVCall0(num uintptr) uintptr
// func SVCall1(num uintptr, a1 interface{}) uintptr
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
//
// The num parameter must be a constant. All other parameters may be any scalar
// value supported by LLVM inline assembly.
@@ -201,15 +201,15 @@ func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, 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:
//
// func (csr CSR) Get() uintptr
// func (csr CSR) Set(uintptr)
// func (csr CSR) SetBits(uintptr) uintptr
// func (csr CSR) ClearBits(uintptr) uintptr
// func (csr CSR) Get() uintptr
// func (csr CSR) Set(uintptr)
// func (csr CSR) SetBits(uintptr) uintptr
// func (csr CSR) ClearBits(uintptr) uintptr
//
// The csr parameter (method receiver) must be a constant. Other parameter can
// be any value.
@@ -226,24 +226,24 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, nil, false)
asm := fmt.Sprintf("csrr $0, %d", csr)
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":
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
asm := fmt.Sprintf("csrw %d, $0", csr)
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":
// Note: it may be possible to optimize this to csrrsi in many cases.
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
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":
// Note: it may be possible to optimize this to csrrci in many cases.
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
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:
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
}
+191 -321
View File
@@ -6,7 +6,6 @@ package compiler
// interface-lowering.go for more details.
import (
"fmt"
"go/token"
"go/types"
"strconv"
@@ -16,49 +15,6 @@ import (
"tinygo.org/x/go-llvm"
)
// Type kinds for basic types.
// They must match the constants for the Kind type in src/reflect/type.go.
var basicTypes = [...]uint8{
types.Bool: 1,
types.Int: 2,
types.Int8: 3,
types.Int16: 4,
types.Int32: 5,
types.Int64: 6,
types.Uint: 7,
types.Uint8: 8,
types.Uint16: 9,
types.Uint32: 10,
types.Uint64: 11,
types.Uintptr: 12,
types.Float32: 13,
types.Float64: 14,
types.Complex64: 15,
types.Complex128: 16,
types.String: 17,
types.UnsafePointer: 18,
}
// These must also match the constants for the Kind type in src/reflect/type.go.
const (
typeKindChan = 19
typeKindInterface = 20
typeKindPointer = 21
typeKindSlice = 22
typeKindArray = 23
typeKindSignature = 24
typeKindMap = 25
typeKindStruct = 26
)
// Flags stored in the first byte of the struct field byte array. Must be kept
// up to date with src/reflect/type.go.
const (
structFieldFlagAnonymous = 1 << iota
structFieldFlagHasTag
structFieldFlagIsExported
)
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
// It tries to put the type in the interface value, but if that's not possible,
// it will do an allocation of the right size and put that in the interface
@@ -67,9 +23,10 @@ const (
// An interface value is a {typecode, value} tuple named runtime._interface.
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
itfValue := b.emitPointerPack([]llvm.Value{val})
itfType := b.getTypeCode(typ)
itfTypeCodeGlobal := b.getTypeCode(typ)
itfTypeCode := b.CreatePtrToInt(itfTypeCodeGlobal, b.uintptrType, "")
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
itf = b.CreateInsertValue(itf, itfType, 0, "")
itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
itf = b.CreateInsertValue(itf, itfValue, 1, "")
return itf
}
@@ -84,236 +41,118 @@ func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type)
}
// getTypeCode returns a reference to a type code.
// A type code is a pointer to a constant global that describes the type.
// This function returns a pointer to the 'kind' field (which might not be the
// first field in the struct).
// It returns a pointer to an external global which should be replaced with the
// real type in the interface lowering pass.
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
ms := c.program.MethodSets.MethodSet(typ)
hasMethodSet := ms.Len() != 0
if _, ok := typ.Underlying().(*types.Interface); ok {
hasMethodSet = false
}
globalName := "reflect/types.type:" + getTypeCodeName(typ)
global := c.mod.NamedGlobal(globalName)
if global.IsNil() {
var typeFields []llvm.Value
// Define the type fields. These must match the structs in
// src/reflect/type.go (ptrType, arrayType, etc). See the comment at the
// top of src/reflect/type.go for more information on the layout of these structs.
typeFieldTypes := []*types.Var{
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
}
// Create a new typecode global.
global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
// Some type classes contain more information for underlying types or
// element types. Store it directly in the typecode global to make
// reflect lowering simpler.
var references llvm.Value
var length int64
var methodSet llvm.Value
var ptrTo llvm.Value
var typeAssert llvm.Value
switch typ := typ.(type) {
case *types.Basic:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
)
case *types.Named:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
)
case *types.Chan, *types.Slice:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
)
case *types.Pointer:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
)
case *types.Array:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
)
case *types.Map:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
)
case *types.Struct:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
)
case *types.Interface:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
)
// TODO: methods
case *types.Signature:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
)
// TODO: signature params and return values
}
if hasMethodSet {
// This method set is appended at the start of the struct. It is
// removed in the interface lowering pass.
// TODO: don't remove these and instead do what upstream Go is doing
// instead. See: https://research.swtch.com/interfaces. This can
// likely be optimized in LLVM using
// https://llvm.org/docs/TypeMetadata.html.
typeFieldTypes = append([]*types.Var{
types.NewVar(token.NoPos, nil, "methodSet", types.Typ[types.UnsafePointer]),
}, typeFieldTypes...)
}
globalType := types.NewStruct(typeFieldTypes, nil)
global = llvm.AddGlobal(c.mod, c.getLLVMType(globalType), globalName)
metabyte := getTypeKind(typ)
switch typ := typ.(type) {
case *types.Basic:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
case *types.Named:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying
}
metabyte |= 1 << 5 // "named" flag
references = c.getTypeCode(typ.Underlying())
case *types.Chan:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elementType
}
case *types.Slice:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elementType
}
references = c.getTypeCode(typ.Elem())
case *types.Pointer:
typeFields = []llvm.Value{c.getTypeCode(typ.Elem())}
references = c.getTypeCode(typ.Elem())
case *types.Slice:
references = c.getTypeCode(typ.Elem())
case *types.Array:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elementType
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
}
case *types.Map:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
}
references = c.getTypeCode(typ.Elem())
length = typ.Len()
case *types.Struct:
typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
c.getTypeCode(types.NewPointer(typ)), // ptrTo
}
structFieldType := c.getLLVMRuntimeType("structField")
var fields []llvm.Value
for i := 0; i < typ.NumFields(); i++ {
field := typ.Field(i)
var flags uint8
if field.Anonymous() {
flags |= structFieldFlagAnonymous
}
if typ.Tag(i) != "" {
flags |= structFieldFlagHasTag
}
if token.IsExported(field.Name()) {
flags |= structFieldFlagIsExported
}
data := string(flags) + field.Name() + "\x00"
if typ.Tag(i) != "" {
if len(typ.Tag(i)) > 0xff {
c.addError(field.Pos(), fmt.Sprintf("struct tag is %d bytes which is too long, max is 255", len(typ.Tag(i))))
}
data += string([]byte{byte(len(typ.Tag(i)))}) + typ.Tag(i)
}
dataInitializer := c.ctx.ConstString(data, false)
dataGlobal := llvm.AddGlobal(c.mod, dataInitializer.Type(), globalName+"."+field.Name())
dataGlobal.SetInitializer(dataInitializer)
dataGlobal.SetAlignment(1)
dataGlobal.SetUnnamedAddr(true)
dataGlobal.SetLinkage(llvm.InternalLinkage)
dataGlobal.SetGlobalConstant(true)
fieldType := c.getTypeCode(field.Type())
fields = append(fields, llvm.ConstNamedStruct(structFieldType, []llvm.Value{
fieldType,
llvm.ConstGEP(dataGlobal.GlobalValueType(), dataGlobal, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
}),
}))
}
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
// Take a pointer to the typecodeID of the first field (if it exists).
structGlobal := c.makeStructTypeFields(typ)
references = llvm.ConstBitCast(structGlobal, global.Type())
case *types.Interface:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: methods
case *types.Signature:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: params, return values, etc
methodSetGlobal := c.getInterfaceMethodSet(typ)
references = llvm.ConstBitCast(methodSetGlobal, global.Type())
}
// Prepend metadata byte.
typeFields = append([]llvm.Value{
llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false),
}, typeFields...)
if hasMethodSet {
typeFields = append([]llvm.Value{
llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType),
}, typeFields...)
if _, ok := typ.Underlying().(*types.Interface); !ok {
methodSet = c.getTypeMethodSet(typ)
} else {
typeAssert = c.getInterfaceImplementsFunc(typ)
typeAssert = llvm.ConstPtrToInt(typeAssert, c.uintptrType)
}
if _, ok := typ.Underlying().(*types.Pointer); !ok {
ptrTo = c.getTypeCode(types.NewPointer(typ))
}
globalValue := llvm.ConstNull(global.Type().ElementType())
if !references.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
}
if length != 0 {
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
}
if !methodSet.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
}
if !ptrTo.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
}
if !typeAssert.IsNil() {
globalValue = llvm.ConstInsertValue(globalValue, typeAssert, []uint32{4})
}
alignment := c.targetData.TypeAllocSize(c.i8ptrType)
globalValue := c.ctx.ConstStruct(typeFields, false)
global.SetInitializer(globalValue)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetGlobalConstant(true)
global.SetAlignment(int(alignment))
if c.Debug {
file := c.getDIFile("<Go type>")
diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
Name: "type " + typ.String(),
File: file,
Line: 1,
Type: c.getDIType(globalType),
LocalToUnit: false,
Expr: c.dibuilder.CreateExpression(nil),
AlignInBits: uint32(alignment * 8),
}
return global
}
// makeStructTypeFields creates a new global that stores all type information
// related to this struct type, and returns the resulting global. This global is
// actually an array of all the fields in the structs.
func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
// The global is an array of runtime.structField structs.
runtimeStructField := c.getLLVMRuntimeType("structField")
structGlobalType := llvm.ArrayType(runtimeStructField, typ.NumFields())
structGlobal := llvm.AddGlobal(c.mod, structGlobalType, "reflect/types.structFields")
structGlobalValue := llvm.ConstNull(structGlobalType)
for i := 0; i < typ.NumFields(); i++ {
fieldGlobalValue := llvm.ConstNull(runtimeStructField)
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), []uint32{0})
fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type())
fieldName.SetLinkage(llvm.PrivateLinkage)
fieldName.SetUnnamedAddr(true)
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
})
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
if typ.Tag(i) != "" {
fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type())
fieldTag.SetLinkage(llvm.PrivateLinkage)
fieldTag.SetUnnamedAddr(true)
fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
})
global.AddMetadata(0, diglobal)
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
}
if typ.Field(i).Embedded() {
fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldEmbedded, []uint32{3})
}
structGlobalValue = llvm.ConstInsertValue(structGlobalValue, fieldGlobalValue, []uint32{uint32(i)})
}
offset := uint64(0)
if hasMethodSet {
// The pointer to the method set is always the first element of the
// global (if there is a method set). However, the pointer we return
// should point to the 'kind' field not the method set.
offset = 1
}
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 0, false),
llvm.ConstInt(llvm.Int32Type(), offset, false),
})
structGlobal.SetInitializer(structGlobalValue)
structGlobal.SetUnnamedAddr(true)
structGlobal.SetLinkage(llvm.PrivateLinkage)
return structGlobal
}
// getTypeKind returns the type kind for the given type, as defined by
// reflect.Kind.
func getTypeKind(t types.Type) uint8 {
switch t := t.Underlying().(type) {
case *types.Basic:
return basicTypes[t.Kind()]
case *types.Chan:
return typeKindChan
case *types.Interface:
return typeKindInterface
case *types.Pointer:
return typeKindPointer
case *types.Slice:
return typeKindSlice
case *types.Array:
return typeKindArray
case *types.Signature:
return typeKindSignature
case *types.Map:
return typeKindMap
case *types.Struct:
return typeKindStruct
default:
panic("unknown type")
}
}
var basicTypeNames = [...]string{
var basicTypes = [...]string{
types.Bool: "bool",
types.Int: "int",
types.Int8: "int8",
@@ -344,7 +183,7 @@ func getTypeCodeName(t types.Type) string {
case *types.Array:
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
case *types.Basic:
return "basic:" + basicTypeNames[t.Kind()]
return "basic:" + basicTypes[t.Kind()]
case *types.Chan:
return "chan:" + getTypeCodeName(t.Elem())
case *types.Interface:
@@ -396,40 +235,75 @@ func getTypeCodeName(t types.Type) string {
// getTypeMethodSet returns a reference (GEP) to a global method set. This
// method set should be unreferenced after the interface lowering pass.
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
globalName := typ.String() + "$methodset"
global := c.mod.NamedGlobal(globalName)
if global.IsNil() {
ms := c.program.MethodSets.MethodSet(typ)
// Create method set.
var signatures, wrappers []llvm.Value
for i := 0; i < ms.Len(); i++ {
method := ms.At(i)
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
signatures = append(signatures, signatureGlobal)
fn := c.program.MethodValue(method)
llvmFnType, llvmFn := c.getFunction(fn)
if llvmFn.IsNil() {
// compiler error, so panic
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
}
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
wrappers = append(wrappers, wrapper)
}
// Construct global value.
globalValue := c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
llvm.ConstArray(c.i8ptrType, signatures),
c.ctx.ConstStruct(wrappers, false),
}, false)
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
global.SetInitializer(globalValue)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global := c.mod.NamedGlobal(typ.String() + "$methodset")
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
if !global.IsNil() {
// the method set already exists
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
}
return global
ms := c.program.MethodSets.MethodSet(typ)
if ms.Len() == 0 {
// no methods, so can leave that one out
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
}
methods := make([]llvm.Value, ms.Len())
interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
for i := 0; i < ms.Len(); i++ {
method := ms.At(i)
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
fn := c.program.MethodValue(method)
llvmFn := c.getFunction(fn)
if llvmFn.IsNil() {
// compiler error, so panic
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
}
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFn)
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal,
llvm.ConstPtrToInt(wrapper, c.uintptrType),
})
methods[i] = methodInfo
}
arrayType := llvm.ArrayType(interfaceMethodInfoType, len(methods))
value := llvm.ConstArray(interfaceMethodInfoType, methods)
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
global.SetInitializer(value)
global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
}
// getInterfaceMethodSet returns a global variable with the method set of the
// given named interface type. This method set is used by the interface lowering
// pass.
func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
name := typ.String()
if _, ok := typ.(*types.Named); !ok {
// Anonymous interface.
name = "reflect/types.interface:" + name
}
global := c.mod.NamedGlobal(name + "$interface")
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
if !global.IsNil() {
// method set already exist, return it
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
}
// Every method is a *i8 reference indicating the signature of this method.
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
for i := range methods {
method := typ.Underlying().(*types.Interface).Method(i)
methods[i] = c.getMethodSignature(method)
}
value := llvm.ConstArray(c.i8ptrType, methods)
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
global.SetInitializer(value)
global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
}
// getMethodSignatureName returns a unique name (that can be used as the name of
@@ -487,25 +361,20 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// implements each method of the interface. See:
// https://research.swtch.com/interfaces
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
commaOk = b.CreateCall(fn, []llvm.Value{actualTypeNum}, "")
} else {
if b.LTO {
assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType)
commaOk = b.CreateICmp(llvm.IntEQ, actualTypeNum, assertedTypeCodeGlobal, "commaok")
} else {
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
if assertedTypeCodeGlobal.IsNil() {
// Create a new typecode global.
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
assertedTypeCodeGlobal.SetGlobalConstant(true)
}
// Type assert on concrete type.
// Call runtime.typeAssert, which will be lowered to a simple icmp
// or const false in the interface lowering pass.
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
if assertedTypeCodeGlobal.IsNil() {
// Create a new typecode global.
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
assertedTypeCodeGlobal.SetGlobalConstant(true)
}
// Type assert on concrete type.
// Call runtime.typeAssert, which will be lowered to a simple icmp or
// const false in the interface lowering pass.
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
}
// Add 2 new basic blocks (that should get optimized away): one for the
@@ -520,8 +389,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// value.
prevBlock := b.GetInsertBlock()
okBlock := b.insertBasicBlock("typeassert.ok")
nextBlock := b.insertBasicBlock("typeassert.next")
okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok")
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.next")
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
b.CreateCondBr(commaOk, okBlock, nextBlock)
@@ -574,7 +443,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll
fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false)
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn)
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
@@ -595,8 +464,8 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
for i := 0; i < sig.Params().Len(); i++ {
paramTuple = append(paramTuple, sig.Params().At(i))
}
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
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)).ElementType()
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn)
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
@@ -611,7 +480,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
// 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
// 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"
wrapper := c.mod.NamedFunction(wrapperName)
if !wrapper.IsNil() {
@@ -636,8 +505,9 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
}
// create wrapper function
paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
fnType := llvmFn.Type().ElementType()
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
c.addStandardAttributes(wrapper)
@@ -664,11 +534,11 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
if llvmFnType.ReturnType().TypeKind() == llvm.VoidTypeKind {
b.CreateCall(llvmFnType, llvmFn, params, "")
if llvmFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
b.CreateCall(llvmFn, params, "")
b.CreateRetVoid()
} else {
ret := b.CreateCall(llvmFnType, llvmFn, params, "ret")
ret := b.CreateCall(llvmFn, params, "ret")
b.CreateRet(ret)
}
@@ -680,8 +550,8 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
// internally to match interfaces and to call the correct method on an
// interface. Examples:
//
// String() string
// Read([]byte) (int, error)
// String() string
// Read([]byte) (int, error)
func methodSignature(method *types.Func) string {
return method.Name() + signature(method.Type().(*types.Signature))
}
@@ -689,8 +559,8 @@ func methodSignature(method *types.Func) string {
// Make a readable version of a function (pointer) signature.
// Examples:
//
// () string
// (string, int) (int, error)
// () string
// (string, int) (int, error)
func signature(sig *types.Signature) string {
s := ""
if sig.Params().Len() == 0 {
@@ -732,7 +602,7 @@ func typestring(t types.Type) string {
case *types.Array:
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
case *types.Basic:
return basicTypeNames[t.Kind()]
return basicTypes[t.Kind()]
case *types.Chan:
switch t.Dir() {
case types.SendRecv:
+6 -10
View File
@@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Fall back to a generic error.
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)
// Create a new global of type runtime/interrupt.handle. Globals of this
@@ -45,17 +45,13 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
globalLLVMType := b.getLLVMType(globalType)
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
if !b.LTO {
global.SetVisibility(llvm.HiddenVisibility)
}
global.SetVisibility(llvm.HiddenVisibility)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
initializer := llvm.ConstNull(globalLLVMType)
initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{
llvm.ConstInt(b.intType, uint64(id.Int64()), true),
}), 2, "")
initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1})
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0})
global.SetInitializer(initializer)
// Add debug info to the interrupt global.
@@ -89,7 +85,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
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
+52 -104
View File
@@ -3,154 +3,102 @@ package compiler
// This file contains helper functions to create calls to LLVM intrinsics.
import (
"go/token"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// Define unimplemented intrinsic functions.
//
// Some functions are either normally implemented in Go assembly (like
// sync/atomic functions) or intentionally left undefined to be implemented
// directly in the compiler (like runtime/volatile functions). Either way, look
// for these and implement them if this is the case.
func (b *builder) defineIntrinsicFunction() {
name := b.fn.RelString(nil)
switch {
case name == "runtime.memcpy" || name == "runtime.memmove":
b.createMemoryCopyImpl()
case name == "runtime.memzero":
b.createMemoryZeroImpl()
case name == "runtime.KeepAlive":
b.createKeepAliveImpl()
case strings.HasPrefix(name, "runtime/volatile.Load"):
b.createVolatileLoad()
case strings.HasPrefix(name, "runtime/volatile.Store"):
b.createVolatileStore()
case strings.HasPrefix(name, "sync/atomic.") && token.IsExported(b.fn.Name()):
b.createFunctionStart(true)
returnValue := b.createAtomicOp(b.fn.Name())
if !returnValue.IsNil() {
b.CreateRet(returnValue)
} else {
b.CreateRetVoid()
}
}
}
// createMemoryCopyImpl creates a call to a builtin LLVM memcpy or memmove
// createMemoryCopyCall creates a call to a builtin LLVM memcpy or memmove
// function, declaring this function if needed. These calls are treated
// specially by optimization passes possibly resulting in better generated code,
// and will otherwise be lowered to regular libc memcpy/memmove calls.
func (b *builder) createMemoryCopyImpl() {
b.createFunctionStart(true)
fnName := "llvm." + b.fn.Name() + ".p0.p0.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())
}
func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm.Value, error) {
fnName := "llvm." + fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
}
var params []llvm.Value
for _, param := range b.fn.Params {
for _, param := range args {
params = append(params, b.getValue(param))
}
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
b.CreateRetVoid()
b.CreateCall(llvmFn, params, "")
return llvm.Value{}, nil
}
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
// createMemoryZeroCall creates calls to llvm.memset.* to zero a block of
// memory, declaring the function if needed. These calls will be lowered to
// regular libc memset calls if they aren't optimized out in a different way.
func (b *builder) createMemoryZeroImpl() {
b.createFunctionStart(true)
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
}
func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
}
params := []llvm.Value{
b.getValue(b.fn.Params[0]),
b.getValue(args[0]),
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
b.getValue(b.fn.Params[1]),
b.getValue(args[1]),
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
}
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
b.CreateRetVoid()
}
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
// using inline assembly.
func (b *builder) createKeepAliveImpl() {
b.createFunctionStart(true)
// Get the underlying value of the interface value.
interfaceValue := b.getValue(b.fn.Params[0])
pointerValue := b.CreateExtractValue(interfaceValue, 1, "")
// Create an equivalent of the following C code, which is basically just a
// nop but ensures the pointerValue is kept alive:
//
// __asm__ __volatile__("" : : "r"(pointerValue))
//
// It should be portable to basically everything as the "r" register type
// exists basically everywhere.
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false)
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
b.CreateRetVoid()
b.CreateCall(llvmFn, params, "")
return llvm.Value{}, nil
}
var mathToLLVMMapping = map[string]string{
"math.Ceil": "llvm.ceil.f64",
"math.Exp": "llvm.exp.f64",
"math.Exp2": "llvm.exp2.f64",
"math.Floor": "llvm.floor.f64",
"math.Log": "llvm.log.f64",
"math.Sqrt": "llvm.sqrt.f64",
"math.Floor": "llvm.floor.f64",
"math.Ceil": "llvm.ceil.f64",
"math.Trunc": "llvm.trunc.f64",
}
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
// instead of the regular Go implementation. This allows LLVM to reason about
// the math operation and (depending on the architecture) allows it to lower the
// operation to very fast floating point instructions. If this is not possible,
// LLVM will emit a call to a libm function that implements the same operation.
//
// One example of an optimization that LLVM can do is to convert
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
// beneficial on architectures where 64-bit floating point operations are (much)
// more expensive than 32-bit ones.
func (b *builder) defineMathOp() {
b.createFunctionStart(true)
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
if llvmName == "" {
panic("unreachable: unknown math operation") // sanity check
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
// possible. It returns the call result if possible, and a boolean whether it
// succeeded. If it doesn't succeed, the architecture doesn't support the given
// intrinsic.
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
// Check whether this intrinsic is supported on the given GOARCH.
// If it is unsupported, this can have two reasons:
//
// 1. LLVM can expand the intrinsic inline (using float instructions), but
// the result doesn't pass the tests of the math package.
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
// libm function call, but the libm function call also fails the math
// package tests.
//
// Whatever the implementation, it must pass the tests in the math package
// so unfortunately only the below intrinsic+architecture combinations are
// supported.
name := call.StaticCallee().RelString(nil)
switch name {
case "math.Ceil", "math.Floor", "math.Trunc":
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
return llvm.Value{}, false
}
case "math.Sqrt":
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
return llvm.Value{}, false
}
default:
return llvm.Value{}, false // only the above functions are supported.
}
llvmFn := b.mod.NamedFunction(llvmName)
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
if llvmFn.IsNil() {
// The intrinsic doesn't exist yet, so declare it.
// At the moment, all supported intrinsics have the form "double
// foo(double %x)" so we can hardcode the signature here.
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
}
// Create a call to the intrinsic.
args := make([]llvm.Value, len(b.fn.Params))
for i, param := range b.fn.Params {
args[i] = b.getValue(param)
args := make([]llvm.Value, len(call.Args))
for i, arg := range call.Args {
args[i] = b.getValue(arg)
}
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
b.CreateRet(result)
return b.CreateCall(llvmFn, args, ""), true
}
+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())
}
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:
// check element type
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
+6 -225
View File
@@ -5,7 +5,6 @@ import (
"go/token"
"go/types"
"math/big"
"strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm"
@@ -24,23 +23,6 @@ func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitca
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
}
// insertBasicBlock inserts a new basic block after the current basic block.
// This is useful when inserting new basic blocks while converting a
// *ssa.BasicBlock to a llvm.BasicBlock and the LLVM basic block needs some
// extra blocks.
// It does not update b.blockExits, this must be done by the caller.
func (b *builder) insertBasicBlock(name string) llvm.BasicBlock {
currentBB := b.Builder.GetInsertBlock()
nextBB := llvm.NextBasicBlock(currentBB)
if nextBB.IsNil() {
// Last basic block in the function, so add one to the end.
return b.ctx.AddBasicBlock(b.llvmFn, name)
}
// Insert a basic block before the next basic block - that is, at the
// current insert location.
return b.ctx.InsertBasicBlock(nextBB, name)
}
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
// llvm.lifetime.end intrinsic. It is commonly used together with
// createTemporaryAlloca.
@@ -51,175 +33,29 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
// 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 (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
valueTypes := make([]llvm.Type, len(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
}
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.pkg.Path(), b.NeedsStackObjects, values)
}
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
packedType := b.ctx.StructType(valueTypes, false)
// 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
return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes)
}
// 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
// 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))
global := llvm.AddGlobal(c.mod, globalType, name)
value := llvm.Undef(globalType)
for i := 0; i < len(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)
return globalType, global
return global
}
// createObjectLayout returns a LLVM value (of type i8*) that describes where
@@ -230,8 +66,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
// 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.
//
// 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 {
// Use the element type for arrays. This works even for nested arrays.
for {
@@ -314,7 +148,6 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// Create the global initializer.
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
bitmap.FillBytes(bitmapBytes)
reverseBytes(bitmapBytes) // big-endian to little-endian
var bitmapByteValues []llvm.Value
for _, b := range bitmapBytes {
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
@@ -420,55 +253,3 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
panic("unknown LLVM type")
}
}
// archFamily returns the archtecture from the LLVM triple but with some
// architecture names ("armv6", "thumbv7m", etc) merged into a single
// architecture name ("arm").
func (c *compilerContext) archFamily() string {
arch := strings.Split(c.Triple, "-")[0]
if strings.HasPrefix(arch, "arm64") {
return "aarch64"
}
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
return "arm"
}
return arch
}
// isThumb returns whether we're in ARM or in Thumb mode. It panics if the
// features string is not one for an ARM architecture.
func (c *compilerContext) isThumb() bool {
var isThumb, isNotThumb bool
for _, feature := range strings.Split(c.Features, ",") {
if feature == "+thumb-mode" {
isThumb = true
}
if feature == "-thumb-mode" {
isNotThumb = true
}
}
if isThumb == isNotThumb {
panic("unexpected feature flags")
}
return isThumb
}
// readStackPointer emits a LLVM intrinsic call that returns the current stack
// pointer as an *i8.
func (b *builder) readStackPointer() llvm.Value {
stacksave := b.mod.NamedFunction("llvm.stacksave")
if stacksave.IsNil() {
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
}
return b.CreateCall(stacksave.GlobalValueType(), 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.
package llvmutil
import (
"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
}
import "tinygo.org/x/go-llvm"
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though
// 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)
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
return
}
@@ -70,15 +54,13 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
builder.SetInsertPointBefore(inst)
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
if next := llvm.NextInstruction(inst); !next.IsNil() {
builder.SetInsertPointBefore(next)
} else {
builder.SetInsertPointAtEnd(inst.InstructionParent())
}
fnType, fn = getLifetimeEndFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, bitcast}, "")
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
// createTemporaryAlloca.
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
fnType, fn := getLifetimeEndFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "")
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, ptr}, "")
}
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
// first if it doesn't exist yet.
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
fnName := "llvm.lifetime.start.p0"
if Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.lifetime.start.p0i8"
}
fn := mod.NamedFunction(fnName)
func getLifetimeStartFunc(mod llvm.Module) llvm.Value {
fn := mod.NamedFunction("llvm.lifetime.start.p0i8")
ctx := mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
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
// first if it doesn't exist yet.
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
fnName := "llvm.lifetime.end.p0"
if Major() < 15 {
fnName = "llvm.lifetime.end.p0i8"
}
fn := mod.NamedFunction(fnName)
func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
fn := mod.NamedFunction("llvm.lifetime.end.p0i8")
ctx := mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
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
@@ -195,32 +168,3 @@ func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llv
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
}
+6 -83
View File
@@ -41,8 +41,8 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
}
keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
if expr.Reserve != nil {
@@ -89,7 +89,6 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// growth.
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, mapKeyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
// Fetch the value from the hashmap.
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
@@ -107,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
// value if the key doesn't exist in the hashmap.
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
mapValue := b.CreateLoad(mapValueAlloca, "")
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
if commaOk {
@@ -134,7 +133,6 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
// key can be compared with runtime.memequal
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr, valuePtr}
b.createRuntimeCall("hashmapBinarySet", params, "")
b.emitLifetimeEnd(keyPtr, keySize)
@@ -163,7 +161,6 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
} else if hashmapIsBinaryKey(keyType) {
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr}
b.createRuntimeCall("hashmapBinaryDelete", params, "")
b.emitLifetimeEnd(keyPtr, keySize)
@@ -220,8 +217,8 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
mapKey := b.CreateLoad(mapKeyAlloca, "")
mapValue := b.CreateLoad(mapValueAlloca, "")
if isKeyStoredAsInterface {
// The key is stored as an interface but it isn't of interface type.
@@ -243,8 +240,7 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
}
// Returns true if this key type does not contain strings, interfaces etc., so
// can be compared with runtime.memequal. Note that padding bytes are undef
// and can alter two "equal" structs being equal when compared with memequal.
// can be compared with runtime.memequal.
func hashmapIsBinaryKey(keyType types.Type) bool {
switch keyType := keyType.(type) {
case *types.Basic:
@@ -267,76 +263,3 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
return false
}
}
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
// we handle nested types. Next, we determine if there are any padding bytes before the next
// element and zero those as well.
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
switch llvmType.TypeKind() {
case llvm.IntegerTypeKind:
// no padding bytes
return nil
case llvm.PointerTypeKind:
// mo padding bytes
return nil
case llvm.ArrayTypeKind:
llvmArrayType := llvmType
llvmElemType := llvmType.ElementType()
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this element
b.zeroUndefBytes(llvmElemType, elemPtr)
}
case llvm.StructTypeKind:
llvmStructType := llvmType
numFields := llvmStructType.StructElementTypesCount()
llvmElementTypes := llvmStructType.StructElementTypes()
for i := 0; i < numFields; i++ {
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this field
llvmElemType := llvmElementTypes[i]
b.zeroUndefBytes(llvmElemType, elemPtr)
// zero any padding bytes before the next field, if any
offset := b.targetData.ElementOffset(llvmStructType, i)
storeSize := b.targetData.TypeStoreSize(llvmElemType)
fieldEndOffset := offset + storeSize
var nextOffset uint64
if i < numFields-1 {
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
} else {
// Last field? Next offset is the total size of the allcoate struct.
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
}
if fieldEndOffset != nextOffset {
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
gepPtr := elemPtr
if gepPtr.Type() != b.i8ptrType {
gepPtr = b.CreateBitCast(gepPtr, b.i8ptrType, "") // LLVM 14
}
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), gepPtr, []llvm.Value{llvmStoreSize}, "")
if paddingStart.Type() != b.i8ptrType {
paddingStart = b.CreateBitCast(paddingStart, b.i8ptrType, "") // LLVM 14
}
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
}
}
}
return nil
}
+1 -1
View File
@@ -152,7 +152,7 @@ func (s *stdSizes) Sizeof(T types.Type) int64 {
return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
case *types.Interface:
return s.PtrSize * 2
case *types.Pointer, *types.Chan, *types.Map:
case *types.Pointer:
return s.PtrSize
case *types.Signature:
// Func values in TinyGo are two words in size.
+27 -42
View File
@@ -10,7 +10,6 @@ import (
"strconv"
"strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
@@ -54,11 +53,11 @@ const (
// getFunction returns the LLVM function for the given *ssa.Function, creating
// 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)
llvmFn := c.mod.NamedFunction(info.linkName)
if !llvmFn.IsNil() {
return llvmFn.GlobalValueType(), llvmFn
return llvmFn
}
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
// closures and bound methods, but should be optimized away when not used.
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
@@ -113,8 +112,17 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
for i, info := range paramInfos {
if info.elemSize != 0 {
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize)
if info.flags&paramIsDeferenceableOrNull == 0 {
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)
}
}
@@ -150,38 +158,23 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// 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("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.
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
if info.exported {
if c.archFamily() == "wasm32" {
// We need to add the wasm-import-module and the wasm-import-name
// attributes.
module := info.module
if module == "" {
module = "env"
}
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
// Set the wasm-import-module attribute if the function's module is set.
if info.module != "" {
name := info.importName
if name == "" {
name = info.linkName
// We need to add the wasm-import-module and the wasm-import-name
wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", info.module)
llvmFn.AddFunctionAttr(wasmImportModuleAttr)
// Add the Wasm Import Name, if we are a named wasm import
if info.importName != "" {
wasmImportNameAttr := c.ctx.CreateStringAttribute("wasm-import-name", info.importName)
llvmFn.AddFunctionAttr(wasmImportNameAttr)
}
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
}
nocaptureKind := llvm.AttributeKindID("nocapture")
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
@@ -198,7 +191,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// should be created right away.
// The exception is the package initializer, which does appear in the
// *ssa.Package members and so shouldn't be created here.
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" {
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
irbuilder := c.ctx.NewBuilder()
b := newBuilder(c, irbuilder, fn)
b.createFunction()
@@ -207,7 +200,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
llvmFn.SetUnnamedAddr(true)
}
return fnType, llvmFn
return llvmFn
}
// getFunctionInfo returns information about a function that is not directly
@@ -367,15 +360,7 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
if strings.Split(c.Triple, "-")[0] == "x86_64" {
// Required by the ABI.
if llvmutil.Major() < 15 {
// 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))
}
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
}
}
+41 -8
View File
@@ -16,7 +16,20 @@ import (
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
num := b.getValue(call.Args[0])
switch {
case b.GOARCH == "amd64" && b.GOOS == "linux":
case b.GOARCH == "amd64":
if b.GOOS == "darwin" {
// Darwin adds this magic number to system call numbers:
//
// > Syscall classes for 64-bit system call entry.
// > For 64-bit users, the 32-bit syscall number is partitioned
// > with the high-order bits representing the class and low-order
// > bits being the syscall number within that class.
// > The high-order 32-bits of the 64-bit syscall number are unused.
// > All system classes enter the kernel via the syscall instruction.
//
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
num = b.CreateOr(num, llvm.ConstInt(b.uintptrType, 0x2000000, false), "")
}
// Sources:
// https://stackoverflow.com/a/2538212
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
@@ -44,7 +57,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
constraints += ",~{rcx},~{r11}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, 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":
// Sources:
// syscall(2) man page
@@ -70,7 +83,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
}
fnType := llvm.FunctionType(b.uintptrType, argTypes, 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":
// Implement the EABI system call convention for Linux.
// Source: syscall(2) man page.
@@ -102,7 +115,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
}
fnType := llvm.FunctionType(b.uintptrType, argTypes, 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":
// Source: syscall(2) man page.
args := []llvm.Value{}
@@ -134,7 +147,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
constraints += ",~{x16},~{x17}" // scratch registers
fnType := llvm.FunctionType(b.uintptrType, argTypes, 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:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
}
@@ -166,6 +179,26 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
retval = b.CreateInsertValue(retval, zero, 1, "")
retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
case "darwin":
syscallResult, err := b.createRawSyscall(call)
if err != nil {
return syscallResult, err
}
// Return values: r0, r1 uintptr, err Errno
// Pseudocode:
// var err uintptr
// if syscallResult != 0 {
// err = syscallResult
// }
// return syscallResult, 0, err
zero := llvm.ConstInt(b.uintptrType, 0, false)
hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError")
retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
retval = b.CreateInsertValue(retval, zero, 1, "")
retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
case "windows":
// On Windows, syscall.Syscall* is basically just a function pointer
// call. This is complicated in gc because of stack switching and the
@@ -205,9 +238,9 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
// Note that SetLastError/GetLastError could be replaced with direct
// access to the thread control block, which is probably smaller and
// faster. The Go runtime does this in assembly.
b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
b.CreateCall(setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
syscallResult := b.CreateCall(fnPtr, params, "")
errResult := b.CreateCall(getLastError, nil, "err")
if b.uintptrType != b.ctx.Int32Type() {
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
// conflict with each other.
type kv struct {
v float32
x, y, z int
v float32
}
var kvGlobal kv
func foo() {
func foo(a *kv) {
// Define a new 'kv' type.
type kv struct {
v byte
x, y, z int
v byte
}
// Use this type.
func(b kv) {}(kv{})
func(b *kv) {}(nil)
}
type T1 []T1
type T2 [2]*T2
var a T1
var b T2
+43 -58
View File
@@ -3,43 +3,43 @@ 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 triple = "wasm32-unknown-wasi"
%main.kv = type { float, i32, i32, i32 }
%main.kv.0 = type { i8, i32, i32, i32 }
%main.kv = type { float }
%main.kv.0 = type { i8 }
@main.kvGlobal = hidden global %main.kv zeroinitializer, align 4
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; 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 #0 {
entry:
%0 = add i32 %x, %y
ret i32 %0
}
; Function Attrs: nounwind
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp eq i32 %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(i8* undef) #0
unreachable
divbyzero.next: ; preds = %entry
%1 = icmp eq i32 %y, -1
%2 = icmp eq i32 %x, -2147483648
@@ -47,35 +47,35 @@ divbyzero.next: ; preds = %entry
%4 = select i1 %3, i32 1, i32 %y
%5 = sdiv i32 %x, %4
ret i32 %5
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
declare void @runtime.divideByZeroPanic(ptr) #0
declare void @runtime.divideByZeroPanic(i8*)
; 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 #0 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(i8* undef) #0
unreachable
divbyzero.next: ; preds = %entry
%1 = udiv i32 %x, %y
ret i32 %1
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
; 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 #0 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(i8* undef) #0
unreachable
divbyzero.next: ; preds = %entry
%1 = icmp eq i32 %y, -1
%2 = icmp eq i32 %x, -2147483648
@@ -83,83 +83,79 @@ divbyzero.next: ; preds = %entry
%4 = select i1 %3, i32 1, i32 %y
%5 = srem i32 %x, %4
ret i32 %5
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
; 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 #0 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(i8* undef) #0
unreachable
divbyzero.next: ; preds = %entry
%1 = urem i32 %x, %y
ret i32 %1
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
; 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 #0 {
entry:
%0 = fcmp oeq float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 {
define hidden i1 @main.floatNE(float %x, float %y, i8* %context) unnamed_addr #0 {
entry:
%0 = fcmp une float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 {
define hidden i1 @main.floatLower(float %x, float %y, i8* %context) unnamed_addr #0 {
entry:
%0 = fcmp olt float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context) unnamed_addr #0 {
entry:
%0 = fcmp ole float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 {
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context) unnamed_addr #0 {
entry:
%0 = fcmp ogt float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context) unnamed_addr #0 {
entry:
%0 = fcmp oge float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context) unnamed_addr #0 {
entry:
ret float %x.r
}
; 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 #0 {
entry:
ret float %x.i
}
; Function Attrs: nounwind
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
entry:
%0 = fadd float %x.r, %y.r
%1 = fadd float %x.i, %y.i
@@ -169,7 +165,7 @@ entry:
}
; Function Attrs: nounwind
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, 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 #0 {
entry:
%0 = fsub float %x.r, %y.r
%1 = fsub float %x.i, %y.i
@@ -179,7 +175,7 @@ entry:
}
; Function Attrs: nounwind
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, 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 #0 {
entry:
%0 = fmul float %x.r, %y.r
%1 = fmul float %x.i, %y.i
@@ -193,27 +189,16 @@ entry:
}
; 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 #0 {
entry:
%complit = alloca %main.kv.0, align 8
%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)
call void @"main.foo$1"(%main.kv.0* null, i8* undef)
ret void
}
; Function Attrs: nounwind
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #0 {
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
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
attributes #0 = { nounwind }
+64 -51
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 triple = "wasm32-unknown-wasi"
%runtime.channelBlockedList = type { ptr, ptr, ptr, { ptr, i32, i32 } }
%runtime.chanSelectState = type { ptr, ptr }
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"internal/task.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*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; 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 #0 {
entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
store i32 3, ptr %chan.value, align 4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
%chan.value.bitcast = bitcast i32* %chan.value to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
store i32 3, i32* %chan.value, align 4
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
ret void
}
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
; Function Attrs: argmemonly nofree nosync nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
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*)
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
; Function Attrs: argmemonly nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
; 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 #0 {
entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
%chan.value.bitcast = bitcast i32* %chan.value to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
ret void
}
declare i1 @runtime.chanRecv(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*)
; 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 #0 {
entry:
%complit = alloca {}, align 8
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
%0 = bitcast {}* %complit to i8*
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
ret void
}
; 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 #0 {
entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
ret void
}
; 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 #0 {
entry:
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
%select.send.value = alloca i32, align 4
store i32 1, ptr %select.send.value, align 4
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
store ptr %ch1, ptr %select.states.alloca, align 8
%select.states.alloca.repack1 = getelementptr inbounds %runtime.chanSelectState, ptr %select.states.alloca, i32 0, i32 1
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
%0 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1
store ptr %ch2, ptr %0, align 8
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1, i32 1
store ptr null, ptr %.repack3, align 4
%select.result = call { i32, i1 } @runtime.tryChanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
store i32 1, i32* %select.send.value, align 4
%select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
%.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
%.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
%0 = bitcast i8** %.repack1 to i32**
store i32* %select.send.value, i32** %0, align 4
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
store i8* null, i8** %.repack4, align 4
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef) #0
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
%1 = extractvalue { i32, i1 } %select.result, 0
%2 = icmp eq i32 %1, 0
br i1 %2, label %select.done, label %select.next
@@ -107,9 +122,7 @@ select.body: ; preds = %select.next
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*)
attributes #0 = { "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 #3 = { nounwind }
attributes #0 = { nounwind }
attributes #1 = { argmemonly nofree nosync nounwind willreturn }
-255
View File
@@ -1,255 +0,0 @@
; ModuleID = 'defer.go'
source_filename = "defer.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7m-unknown-unknown-eabi"
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
%runtime._interface = type { ptr, ptr }
%runtime._defer = type { i32, ptr }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
declare void @main.external(ptr) #0
; Function Attrs: nounwind
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
entry:
%defer.alloca = alloca { i32, ptr }, align 4
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3
store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
store ptr null, ptr %defer.alloca.repack15, align 4
store ptr %defer.alloca, ptr %deferPtr, align 4
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
%setjmp.result = icmp eq i32 %setjmp, 0
br i1 %setjmp.result, label %1, label %lpad
1: ; preds = %entry
call void @main.external(ptr undef) #3
br label %rundefers.loophead
rundefers.loophead: ; preds = %3, %1
%2 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %2, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
%stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %2, align 4
switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
%setjmp1 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
%setjmp.result2 = icmp eq i32 %setjmp1, 0
br i1 %setjmp.result2, label %3, label %lpad
3: ; preds = %rundefers.callback0
call void @"main.deferSimple$1"(ptr undef)
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
ret void
recover: ; preds = %rundefers.end3
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
ret void
lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry
br label %rundefers.loophead6
rundefers.loophead6: ; preds = %5, %lpad
%4 = load ptr, ptr %deferPtr, align 4
%stackIsNil7 = icmp eq ptr %4, null
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
rundefers.loop5: ; preds = %rundefers.loophead6
%stack.next.gep8 = getelementptr inbounds %runtime._defer, ptr %4, i32 0, i32 1
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4
store ptr %stack.next9, ptr %deferPtr, align 4
%callback11 = load i32, ptr %4, align 4
switch i32 %callback11, label %rundefers.default4 [
i32 0, label %rundefers.callback012
]
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
%setjmp.result14 = icmp eq i32 %setjmp13, 0
br i1 %setjmp.result14, label %5, label %lpad
5: ; preds = %rundefers.callback012
call void @"main.deferSimple$1"(ptr undef)
br label %rundefers.loophead6
rundefers.default4: ; preds = %rundefers.loop5
unreachable
rundefers.end3: ; preds = %rundefers.loophead6
br label %recover
}
; Function Attrs: nocallback nofree nosync nounwind willreturn
declare ptr @llvm.stacksave() #2
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #0
; Function Attrs: nounwind
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
entry:
call void @runtime.printint32(i32 3, ptr undef) #3
ret void
}
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #0
declare void @runtime.printint32(i32, ptr) #0
; Function Attrs: nounwind
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
entry:
%defer.alloca2 = alloca { i32, ptr }, align 4
%defer.alloca = alloca { i32, ptr }, align 4
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3
store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
store ptr null, ptr %defer.alloca.repack22, align 4
store ptr %defer.alloca, ptr %deferPtr, align 4
store i32 1, ptr %defer.alloca2, align 4
%defer.alloca2.repack23 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca2, i32 0, i32 1
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
store ptr %defer.alloca2, ptr %deferPtr, align 4
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
%setjmp.result = icmp eq i32 %setjmp, 0
br i1 %setjmp.result, label %1, label %lpad
1: ; preds = %entry
call void @main.external(ptr undef) #3
br label %rundefers.loophead
rundefers.loophead: ; preds = %4, %3, %1
%2 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %2, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
%stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %2, align 4
switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0
i32 1, label %rundefers.callback1
]
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
%setjmp.result4 = icmp eq i32 %setjmp3, 0
br i1 %setjmp.result4, label %3, label %lpad
3: ; preds = %rundefers.callback0
call void @"main.deferMultiple$1"(ptr undef)
br label %rundefers.loophead
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
%setjmp.result6 = icmp eq i32 %setjmp5, 0
br i1 %setjmp.result6, label %4, label %lpad
4: ; preds = %rundefers.callback1
call void @"main.deferMultiple$2"(ptr undef)
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
ret void
recover: ; preds = %rundefers.end7
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
ret void
lpad: ; preds = %rundefers.callback119, %rundefers.callback016, %rundefers.callback1, %rundefers.callback0, %entry
br label %rundefers.loophead10
rundefers.loophead10: ; preds = %7, %6, %lpad
%5 = load ptr, ptr %deferPtr, align 4
%stackIsNil11 = icmp eq ptr %5, null
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
rundefers.loop9: ; preds = %rundefers.loophead10
%stack.next.gep12 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4
store ptr %stack.next13, ptr %deferPtr, align 4
%callback15 = load i32, ptr %5, align 4
switch i32 %callback15, label %rundefers.default8 [
i32 0, label %rundefers.callback016
i32 1, label %rundefers.callback119
]
rundefers.callback016: ; preds = %rundefers.loop9
%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
%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
br i1 %setjmp.result21, label %7, label %lpad
7: ; preds = %rundefers.callback119
call void @"main.deferMultiple$2"(ptr undef)
br label %rundefers.loophead10
rundefers.default8: ; preds = %rundefers.loop9
unreachable
rundefers.end7: ; preds = %rundefers.loophead10
br label %recover
}
; Function Attrs: nounwind
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
entry:
call void @runtime.printint32(i32 3, ptr undef) #3
ret void
}
; Function Attrs: nounwind
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
entry:
call void @runtime.printint32(i32 5, ptr undef) #3
ret void
}
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #2 = { nocallback nofree nosync nounwind willreturn }
attributes #3 = { nounwind }
attributes #4 = { nounwind returns_twice }
-20
View File
@@ -1,20 +0,0 @@
package main
func external()
func deferSimple() {
defer func() {
print(3)
}()
external()
}
func deferMultiple() {
defer func() {
print(3)
}()
defer func() {
print(5)
}()
external()
}
+12 -13
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 triple = "wasm32-unknown-wasi"
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; 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 #0 {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -26,25 +26,25 @@ entry:
}
; Function Attrs: nounwind
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 {
define hidden float @main.maxu32f(i8* %context) unnamed_addr #0 {
entry:
ret float 0x41F0000000000000
}
; Function Attrs: nounwind
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 {
define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #0 {
entry:
ret i32 -1
}
; 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 #0 {
entry:
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
}
; Function Attrs: nounwind
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 {
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context) unnamed_addr #0 {
entry:
%0 = uitofp i32 %v to float
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
@@ -54,7 +54,7 @@ entry:
}
; Function Attrs: nounwind
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 {
define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #0 {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -67,7 +67,7 @@ entry:
}
; Function Attrs: nounwind
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 {
define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #0 {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 2.550000e+02
@@ -79,7 +79,7 @@ entry:
}
; Function Attrs: nounwind
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 {
define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #0 {
entry:
%abovemin = fcmp oge float %v, -1.280000e+02
%belowmax = fcmp ole float %v, 1.270000e+02
@@ -92,5 +92,4 @@ entry:
ret i8 %0
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { nounwind }
+16 -17
View File
@@ -3,46 +3,45 @@ source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; 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 #0 {
entry:
%0 = icmp eq ptr %callback.funcptr, null
%0 = icmp eq void ()* %callback.funcptr, null
br i1 %0, label %fpcall.throw, label %fpcall.next
fpcall.next: ; preds = %entry
call void %callback.funcptr(i32 3, ptr %callback.context) #2
ret void
fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(ptr undef) #2
call void @runtime.nilPanic(i8* undef) #0
unreachable
fpcall.next: ; preds = %entry
%1 = bitcast void ()* %callback.funcptr to void (i32, i8*)*
call void %1(i32 3, i8* %callback.context) #0
ret void
}
declare void @runtime.nilPanic(ptr) #0
declare void @runtime.nilPanic(i8*)
; Function Attrs: nounwind
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
define hidden void @main.bar(i8* %context) unnamed_addr #0 {
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
}
; 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 #0 {
entry:
ret void
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
attributes #0 = { nounwind }
+93 -97
View File
@@ -3,137 +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 triple = "wasm32-unknown-wasi"
%runtime._interface = type { ptr, ptr }
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
%runtime.interfaceMethodInfo = type { i8*, i32 }
%runtime._interface = type { i32, i8* }
@main.scalar1 = hidden global ptr null, align 4
@main.scalar2 = hidden global ptr null, align 4
@main.scalar3 = hidden global ptr null, align 4
@main.scalar4 = hidden global ptr null, align 4
@main.array1 = hidden global ptr null, align 4
@main.array2 = hidden global ptr null, align 4
@main.array3 = hidden global ptr null, align 4
@main.struct1 = hidden global ptr null, align 4
@main.struct2 = hidden global ptr null, align 4
@main.struct3 = hidden global ptr null, align 4
@main.struct4 = hidden global ptr null, align 4
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
@main.slice3 = hidden global { ptr, 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-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 16, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:complex128" }, align 4
@main.scalar1 = hidden global i8* null, align 4
@main.scalar2 = hidden global i32* null, align 4
@main.scalar3 = hidden global i64* null, align 4
@main.scalar4 = hidden global float* null, align 4
@main.array1 = hidden global [3 x i8]* null, align 4
@main.array2 = hidden global [71 x i8]* null, align 4
@main.array3 = hidden global [3 x i8*]* null, align 4
@main.struct1 = hidden global {}* null, align 4
@main.struct2 = hidden global { i32, i32 }* null, align 4
@main.struct3 = hidden global { i8*, [60 x i32], i8* }* null, align 4
@main.struct4 = hidden global { i8*, [61 x i32] }* null, align 4
@main.slice1 = hidden global { i8*, i32, i32 } zeroinitializer, align 8
@main.slice2 = hidden global { i32**, 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" \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"\00\00\00\00\00\00\00\01" }
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:complex128", i32 0 }
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:complex128", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
define hidden void @main.newScalar(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
%new = call ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new, ptr @main.scalar1, align 4
%new1 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new1, ptr @main.scalar2, align 4
%new2 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new2, ptr @main.scalar3, align 4
%new3 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new3, ptr @main.scalar4, align 4
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
store i8* %new, i8** @main.scalar1, align 4
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4
%new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4
%new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
ret void
}
; Function Attrs: nounwind
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
define hidden void @main.newArray(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
%new = call ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new, ptr @main.array1, align 4
%new1 = call ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new1, ptr @main.array2, align 4
%new2 = call ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new2, ptr @main.array3, align 4
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4
%new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4
%new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
ret void
}
; Function Attrs: nounwind
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
define hidden void @main.newStruct(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
%new = call ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new, ptr @main.struct1, align 4
%new1 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new1, ptr @main.struct2, align 4
%new2 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new2, ptr @main.struct3, align 4
%new3 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2
store ptr %new3, ptr @main.struct4, align 4
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4
%new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4
%new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
%new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
store i8* %new3, i8** bitcast ({ i8*, [61 x i32] }** @main.struct4 to i8**), align 4
ret void
}
; Function Attrs: nounwind
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
%new = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %new
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #0
%0 = bitcast i8* %new to { i8*, void ()* }*
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
ret { i8*, void ()* }* %0
}
; Function Attrs: nounwind
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
define hidden void @main.makeSlice(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
%makeslice = call ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2
store ptr %makeslice, ptr @main.slice1, align 8
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 8
%makeslice1 = call ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #2
store ptr %makeslice1, ptr @main.slice2, align 8
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 8
%makeslice3 = call ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #2
store ptr %makeslice3, ptr @main.slice3, align 8
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 8
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #0
store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 1), align 4
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 2), align 8
%makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #0
store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 1), align 4
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 2), align 8
%makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #0
store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 1), align 4
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 2), align 8
ret void
}
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
store double %v.r, ptr %0, align 8
%.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
store double %v.i, ptr %.repack1, align 8
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
ret %runtime._interface %1
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%.repack = bitcast i8* %0 to double*
store double %v.r, double* %.repack, align 8
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
%1 = bitcast i8* %.repack1 to double*
store double %v.i, double* %1, align 8
%2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
ret %runtime._interface %2
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
attributes #0 = { nounwind }
-30
View File
@@ -1,30 +0,0 @@
package main
import "unsafe"
type Coord interface {
int | float32
}
type Point[T Coord] struct {
X, Y T
}
func Add[T Coord](a, b Point[T]) Point[T] {
checkSize(unsafe.Alignof(a))
checkSize(unsafe.Sizeof(a))
return Point[T]{
X: a.X + b.X,
Y: a.Y + b.Y,
}
}
func main() {
var af, bf Point[float32]
Add(af, bf)
var ai, bi Point[int]
Add(ai, bi)
}
func checkSize(uintptr)
-259
View File
@@ -1,259 +0,0 @@
; ModuleID = 'generics.go'
source_filename = "generics.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%"main.Point[int]" = type { i32, i32 }
%"main.Point[float32]" = type { float, float }
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.main(i8* %context) unnamed_addr #1 {
entry:
%bi = alloca %"main.Point[int]", align 8
%ai = alloca %"main.Point[int]", align 8
%bf = alloca %"main.Point[float32]", align 8
%af = alloca %"main.Point[float32]", align 8
%af.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
store float 0.000000e+00, float* %af.repack, align 8
%af.repack1 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
store float 0.000000e+00, float* %af.repack1, align 4
%0 = bitcast %"main.Point[float32]"* %af to i8*
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
%bf.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
store float 0.000000e+00, float* %bf.repack, align 8
%bf.repack2 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
store float 0.000000e+00, float* %bf.repack2, align 4
%1 = bitcast %"main.Point[float32]"* %bf to i8*
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
%.unpack = load float, float* %.elt, align 8
%.elt3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
%.unpack4 = load float, float* %.elt3, align 4
%.elt5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
%.unpack6 = load float, float* %.elt5, align 8
%.elt7 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
%.unpack8 = load float, float* %.elt7, align 4
%2 = call %"main.Point[float32]" @"main.Add[float32]"(float %.unpack, float %.unpack4, float %.unpack6, float %.unpack8, i8* undef)
%ai.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
store i32 0, i32* %ai.repack, align 8
%ai.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
store i32 0, i32* %ai.repack9, align 4
%3 = bitcast %"main.Point[int]"* %ai to i8*
call void @runtime.trackPointer(i8* nonnull %3, i8* undef) #2
%bi.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
store i32 0, i32* %bi.repack, align 8
%bi.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
store i32 0, i32* %bi.repack10, align 4
%4 = bitcast %"main.Point[int]"* %bi to i8*
call void @runtime.trackPointer(i8* nonnull %4, i8* undef) #2
%.elt11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
%.unpack12 = load i32, i32* %.elt11, align 8
%.elt13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
%.unpack14 = load i32, i32* %.elt13, align 4
%.elt15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
%.unpack16 = load i32, i32* %.elt15, align 8
%.elt17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
%.unpack18 = load i32, i32* %.elt17, align 4
%5 = call %"main.Point[int]" @"main.Add[int]"(i32 %.unpack12, i32 %.unpack14, i32 %.unpack16, i32 %.unpack18, i8* undef)
ret void
}
; Function Attrs: nounwind
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, i8* %context) unnamed_addr #1 {
entry:
%complit = alloca %"main.Point[float32]", align 8
%b = alloca %"main.Point[float32]", align 8
%a = alloca %"main.Point[float32]", align 8
%a.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
store float 0.000000e+00, float* %a.repack, align 8
%a.repack9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
store float 0.000000e+00, float* %a.repack9, align 4
%0 = bitcast %"main.Point[float32]"* %a to i8*
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
%a.repack10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
store float %a.X, float* %a.repack10, align 8
%a.repack11 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
store float %a.Y, float* %a.repack11, align 4
%b.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
store float 0.000000e+00, float* %b.repack, align 8
%b.repack13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
store float 0.000000e+00, float* %b.repack13, align 4
%1 = bitcast %"main.Point[float32]"* %b to i8*
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
%b.repack14 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
store float %b.X, float* %b.repack14, align 8
%b.repack15 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
store float %b.Y, float* %b.repack15, align 4
call void @main.checkSize(i32 4, i8* undef) #2
call void @main.checkSize(i32 8, i8* undef) #2
%complit.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
store float 0.000000e+00, float* %complit.repack, align 8
%complit.repack17 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
store float 0.000000e+00, float* %complit.repack17, align 4
%2 = bitcast %"main.Point[float32]"* %complit to i8*
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
%3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
br i1 false, label %deref.throw, label %deref.next
deref.next: ; preds = %entry
br i1 false, label %deref.throw1, label %deref.next2
deref.next2: ; preds = %deref.next
%4 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
%5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
%6 = load float, float* %5, align 8
%7 = load float, float* %4, align 8
%8 = fadd float %6, %7
br i1 false, label %deref.throw3, label %deref.next4
deref.next4: ; preds = %deref.next2
br i1 false, label %deref.throw5, label %deref.next6
deref.next6: ; preds = %deref.next4
%9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
%10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
%11 = load float, float* %10, align 4
%12 = load float, float* %9, align 4
br i1 false, label %store.throw, label %store.next
store.next: ; preds = %deref.next6
store float %8, float* %3, align 8
br i1 false, label %store.throw7, label %store.next8
store.next8: ; preds = %store.next
%13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
%14 = fadd float %11, %12
store float %14, float* %13, align 4
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
%.unpack = load float, float* %.elt, align 8
%15 = insertvalue %"main.Point[float32]" undef, float %.unpack, 0
%16 = insertvalue %"main.Point[float32]" %15, float %14, 1
ret %"main.Point[float32]" %16
deref.throw: ; preds = %entry
unreachable
deref.throw1: ; preds = %deref.next
unreachable
deref.throw3: ; preds = %deref.next2
unreachable
deref.throw5: ; preds = %deref.next4
unreachable
store.throw: ; preds = %deref.next6
unreachable
store.throw7: ; preds = %store.next
unreachable
}
declare void @main.checkSize(i32, i8*) #0
declare void @runtime.nilPanic(i8*) #0
; Function Attrs: nounwind
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, i8* %context) unnamed_addr #1 {
entry:
%complit = alloca %"main.Point[int]", align 8
%b = alloca %"main.Point[int]", align 8
%a = alloca %"main.Point[int]", align 8
%a.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
store i32 0, i32* %a.repack, align 8
%a.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
store i32 0, i32* %a.repack9, align 4
%0 = bitcast %"main.Point[int]"* %a to i8*
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
%a.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
store i32 %a.X, i32* %a.repack10, align 8
%a.repack11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
store i32 %a.Y, i32* %a.repack11, align 4
%b.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
store i32 0, i32* %b.repack, align 8
%b.repack13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
store i32 0, i32* %b.repack13, align 4
%1 = bitcast %"main.Point[int]"* %b to i8*
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
%b.repack14 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
store i32 %b.X, i32* %b.repack14, align 8
%b.repack15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
store i32 %b.Y, i32* %b.repack15, align 4
call void @main.checkSize(i32 4, i8* undef) #2
call void @main.checkSize(i32 8, i8* undef) #2
%complit.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
store i32 0, i32* %complit.repack, align 8
%complit.repack17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
store i32 0, i32* %complit.repack17, align 4
%2 = bitcast %"main.Point[int]"* %complit to i8*
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
%3 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
br i1 false, label %deref.throw, label %deref.next
deref.next: ; preds = %entry
br i1 false, label %deref.throw1, label %deref.next2
deref.next2: ; preds = %deref.next
%4 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
%5 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
%6 = load i32, i32* %5, align 8
%7 = load i32, i32* %4, align 8
%8 = add i32 %6, %7
br i1 false, label %deref.throw3, label %deref.next4
deref.next4: ; preds = %deref.next2
br i1 false, label %deref.throw5, label %deref.next6
deref.next6: ; preds = %deref.next4
%9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
%10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
%11 = load i32, i32* %10, align 4
%12 = load i32, i32* %9, align 4
br i1 false, label %store.throw, label %store.next
store.next: ; preds = %deref.next6
store i32 %8, i32* %3, align 8
br i1 false, label %store.throw7, label %store.next8
store.next8: ; preds = %store.next
%13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
%14 = add i32 %11, %12
store i32 %14, i32* %13, align 4
%.elt = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
%.unpack = load i32, i32* %.elt, align 8
%15 = insertvalue %"main.Point[int]" undef, i32 %.unpack, 0
%16 = insertvalue %"main.Point[int]" %15, i32 %14, 1
ret %"main.Point[int]" %16
deref.throw: ; preds = %entry
unreachable
deref.throw1: ; preds = %deref.next
unreachable
deref.throw3: ; preds = %deref.next2
unreachable
deref.throw5: ; preds = %deref.next4
unreachable
store.throw: ; preds = %deref.next6
unreachable
store.throw7: ; preds = %store.next
unreachable
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
+41
View File
@@ -0,0 +1,41 @@
package main
// Test changes to the language introduced in Go 1.17.
// For details, see: https://tip.golang.org/doc/go1.17#language
// These tests should be merged into the regular slice tests once Go 1.17 is the
// minimun Go version for TinyGo.
import "unsafe"
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
return unsafe.Add(p, len)
}
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
return unsafe.Add(p, len)
}
func SliceToArray(s []int) *[4]int {
return (*[4]int)(s)
}
func SliceToArrayConst() *[4]int {
s := make([]int, 6)
return (*[4]int)(s)
}
func SliceInt(ptr *int, len int) []int {
return unsafe.Slice(ptr, len)
}
func SliceUint16(ptr *byte, len uint16) []byte {
return unsafe.Slice(ptr, len)
}
func SliceUint64(ptr *int, len uint64) []int {
return unsafe.Slice(ptr, len)
}
func SliceInt64(ptr *int, len int64) []int {
return unsafe.Slice(ptr, len)
}
+159
View File
@@ -0,0 +1,159 @@
; ModuleID = 'go1.17.go'
source_filename = "go1.17.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = getelementptr i8, i8* %p, i32 %len
call void @runtime.trackPointer(i8* %0, i8* undef) #0
ret i8* %0
}
; Function Attrs: nounwind
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = trunc i64 %len to i32
%1 = getelementptr i8, i8* %p, i32 %0
call void @runtime.trackPointer(i8* %1, i8* undef) #0
ret i8* %1
}
; Function Attrs: nounwind
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ult i32 %s.len, 4
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.throw: ; preds = %entry
call void @runtime.sliceToArrayPointerPanic(i8* undef) #0
unreachable
slicetoarray.next: ; preds = %entry
%1 = bitcast i32* %s.data to [4 x i32]*
ret [4 x i32]* %1
}
declare void @runtime.sliceToArrayPointerPanic(i8*)
; Function Attrs: nounwind
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #0 {
entry:
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #0
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.throw: ; preds = %entry
unreachable
slicetoarray.next: ; preds = %entry
%0 = bitcast i8* %makeslice to [4 x i32]*
ret [4 x i32]* %0
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ugt i32 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i32 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
%8 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %8, i8* undef) #0
ret { i32*, i32, i32 } %7
}
declare void @runtime.unsafeSlicePanic(i8*)
; Function Attrs: nounwind
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp eq i8* %ptr, null
%1 = icmp ne i16 %len, 0
%2 = and i1 %0, %1
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%3 = zext i16 %len to i32
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
ret { i8*, i32, i32 } %6
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
%9 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %9, i8* undef) #0
ret { i32*, i32, i32 } %8
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
%9 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %9, i8* undef) #0
ret { i32*, i32, i32 } %8
}
attributes #0 = { nounwind }
-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 }
+114 -91
View File
@@ -3,185 +3,208 @@ source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "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" }
%"internal/task.gcData" = type {}
%"internal/task.state" = type { i32, i32* }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr 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
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef) #0
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #0
ret void
}
declare void @main.regularFunction(i32, ptr) #0
declare void @main.regularFunction(i32, i8*)
; 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 #1 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #8
%unpack.int = ptrtoint i8* %0 to i32
call void @main.regularFunction(i32 %unpack.int, i8* undef) #0
ret void
}
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #0
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*)
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
declare void @"internal/task.start"(i32, i8*, i32, i8*)
; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr 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
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef) #0
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #0
ret void
}
; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
entry:
ret void
}
; 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 #2 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
%unpack.int = ptrtoint i8* %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
ret void
}
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry:
%n = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8
store i32 3, ptr %n, align 4
%0 = call ptr @runtime.alloc(i32 8, ptr null, ptr undef) #8
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
store ptr %n, ptr %1, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #8
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8
%2 = load i32, ptr %n, align 4
call void @runtime.printint32(i32 %2, ptr undef) #8
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%0 = bitcast i8* %n to i32*
store i32 3, i32* %0, align 4
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #0
%2 = bitcast i8* %1 to i32*
store i32 5, i32* %2, align 4
%3 = getelementptr inbounds i8, i8* %1, i32 4
%4 = bitcast i8* %3 to i8**
store i8* %n, i8** %4, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef) #0
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef) #0
%5 = load i32, i32* %0, align 4
call void @runtime.printint32(i32 %5, i8* undef) #0
ret void
}
; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
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
}
; 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 #3 {
entry:
%1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
%1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
ret void
}
declare void @runtime.printint32(i32, ptr) #0
declare void @runtime.printint32(i32, i8*)
; 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 #0 {
entry:
%0 = call ptr @runtime.alloc(i32 12, ptr null, ptr undef) #8
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
store ptr %fn.funcptr, ptr %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #8
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
%1 = bitcast i8* %0 to i32*
store i32 5, i32* %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4
%3 = bitcast i8* %2 to i8**
store i8* %fn.context, i8** %3, align 4
%4 = getelementptr inbounds i8, i8* %0, i32 8
%5 = bitcast i8* %4 to void ()**
store void ()* %fn.funcptr, void ()** %5, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef) #0
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #0
ret void
}
; 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 #4 {
entry:
%1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
%5 = load ptr, ptr %4, align 4
call void %5(i32 %1, ptr %3) #8
%1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = bitcast i8* %6 to void (i32, i8*)**
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
call void %8(i32 %2, i8* %5) #0
ret void
}
; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
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) #0
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*)
; 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 #0 {
entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #8
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
ret void
}
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %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 #0 {
entry:
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4
%.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
store i32 4, ptr %.repack1, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #8
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
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
%1 = bitcast i8* %0 to i8**
store i8* %itf.value, i8** %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4
%.repack = bitcast i8* %2 to i8**
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
%3 = bitcast i8* %.repack1 to i32*
store i32 4, i32* %3, align 4
%4 = getelementptr inbounds i8, i8* %0, i32 12
%5 = bitcast i8* %4 to i32*
store i32 %itf.typecode, i32* %5, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* undef) #0
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #0
ret void
}
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
; 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 #6 {
entry:
%1 = load ptr, ptr %0, align 4
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
%5 = load i32, ptr %4, align 4
%6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
%7 = load ptr, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8
%1 = bitcast i8* %0 to i8**
%2 = load i8*, i8** %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = bitcast i8* %6 to i32*
%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) #0
ret void
}
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #2 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
attributes #6 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #7 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #8 = { nounwind }
attributes #0 = { nounwind }
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #4 = { nounwind "tinygo-gowrapper" }
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
+122 -101
View File
@@ -3,196 +3,217 @@ 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 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" }
%"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
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
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) #0
ret void
}
declare void @main.regularFunction(i32, ptr) #0
declare void @main.regularFunction(i32, i8*)
declare void @runtime.deadlock(ptr) #0
declare void @runtime.deadlock(i8*)
; 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 #1 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #8
call void @runtime.deadlock(ptr undef) #8
%unpack.int = ptrtoint i8* %0 to i32
call void @main.regularFunction(i32 %unpack.int, i8* undef) #0
call void @runtime.deadlock(i8* undef) #0
unreachable
}
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
declare void @"internal/task.start"(i32, i8*, i32, i8*)
; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
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) #0
ret void
}
; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
entry:
ret void
}
; 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 #2 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
call void @runtime.deadlock(ptr undef) #8
%unpack.int = ptrtoint i8* %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
call void @runtime.deadlock(i8* undef) #0
unreachable
}
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
%n = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8
store i32 3, ptr %n, align 4
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #8
%0 = call ptr @runtime.alloc(i32 8, ptr null, ptr undef) #8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
store ptr %n, ptr %1, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8
%2 = load i32, ptr %n, align 4
call void @runtime.printint32(i32 %2, ptr undef) #8
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%0 = bitcast i8* %n to i32*
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
store i32 3, i32* %0, align 4
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #0
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #0
%2 = bitcast i8* %1 to i32*
store i32 5, i32* %2, align 4
%3 = getelementptr inbounds i8, i8* %1, i32 4
%4 = bitcast i8* %3 to i8**
store i8* %n, i8** %4, align 4
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #0
%5 = load i32, i32* %0, align 4
call void @runtime.printint32(i32 %5, i8* undef) #0
ret void
}
; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
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
}
; 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 #3 {
entry:
%1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
call void @runtime.deadlock(ptr undef) #8
%1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
call void @runtime.deadlock(i8* undef) #0
unreachable
}
declare void @runtime.printint32(i32, ptr) #0
declare void @runtime.printint32(i32, i8*)
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
%0 = call ptr @runtime.alloc(i32 12, ptr null, ptr undef) #8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
store ptr %fn.funcptr, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 16384, ptr undef) #8
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%1 = bitcast i8* %0 to i32*
store i32 5, i32* %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4
%3 = bitcast i8* %2 to i8**
store i8* %fn.context, i8** %3, align 4
%4 = getelementptr inbounds i8, i8* %0, i32 8
%5 = bitcast i8* %4 to void ()**
store void ()* %fn.funcptr, void ()** %5, align 4
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 16384, i8* undef) #0
ret void
}
; 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 #4 {
entry:
%1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
%5 = load ptr, ptr %4, align 4
call void %5(i32 %1, ptr %3) #8
call void @runtime.deadlock(ptr undef) #8
%1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = bitcast i8* %6 to void (i32, i8*)**
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
call void %8(i32 %2, i8* %5) #0
call void @runtime.deadlock(i8* undef) #0
unreachable
}
; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
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) #0
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*)
; 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 #0 {
entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #8
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
ret void
}
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %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 #0 {
entry:
%stackalloc = alloca i8, align 1
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4
%.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
store i32 4, ptr %.repack1, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %2, 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
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%1 = bitcast i8* %0 to i8**
store i8* %itf.value, i8** %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4
%.repack = bitcast i8* %2 to i8**
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
%3 = bitcast i8* %.repack1 to i32*
store i32 4, i32* %3, align 4
%4 = getelementptr inbounds i8, i8* %0, i32 12
%5 = bitcast i8* %4 to i32*
store i32 %itf.typecode, i32* %5, align 4
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 16384, i8* undef) #0
ret void
}
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
; 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 #6 {
entry:
%1 = load ptr, ptr %0, align 4
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
%5 = load i32, ptr %4, align 4
%6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
%7 = load ptr, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8
call void @runtime.deadlock(ptr undef) #8
%1 = bitcast i8* %0 to i8**
%2 = load i8*, i8** %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = bitcast i8* %6 to i32*
%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) #0
call void @runtime.deadlock(i8* undef) #0
unreachable
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #4 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
attributes #6 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #7 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #8 = { nounwind }
attributes #0 = { nounwind }
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #4 = { nounwind "tinygo-gowrapper" }
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
+71 -76
View File
@@ -3,135 +3,130 @@ 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 triple = "wasm32-unknown-wasi"
%runtime._interface = type { ptr, ptr }
%runtime._string = type { ptr, i32 }
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
%runtime.interfaceMethodInfo = type { i8*, i32 }
%runtime._interface = type { i32, i8* }
%runtime._string = type { i8*, i32 }
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 2, ptr @"reflect/types.type:pointer:basic:int" }, align 4
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:int" }, align 4
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:named:error" }, align 4
@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
@"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 { %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 { %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 { %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 { %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/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, 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 { %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/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"]
@"reflect/types.typeid:basic:int" = external constant i8
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
call void @runtime.trackPointer(i8* null, i8* undef) #0
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
call void @runtime.trackPointer(i8* null, i8* undef) #0
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
call void @runtime.trackPointer(i8* null, i8* undef) #0
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
}
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #1
; Function Attrs: nounwind
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #0 {
entry:
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
call void @runtime.trackPointer(i8* null, i8* undef) #0
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
}
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #2
; Function Attrs: nounwind
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry:
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #0
br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.ok: ; preds = %entry
br label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
ret i1 %typecode
}
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*)
; Function Attrs: nounwind
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry:
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
; Function Attrs: nounwind
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
; Function Attrs: nounwind
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
ret i1 %0
typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry:
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #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) #0
ret i8 %0
}
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #4
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #3
; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(ptr %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 #0 {
entry:
%stackalloc = alloca i8, align 1
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #6
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #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) #0
ret %runtime._string %0
}
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #4
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
attributes #3 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
attributes #4 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #5 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #6 = { nounwind }
attributes #0 = { nounwind }
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
+32
View File
@@ -0,0 +1,32 @@
; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7m-unknown-unknown-eabi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @math.Sqrt(double %x, i8* undef) #0
ret double %0
}
declare double @math.Sqrt(double, i8*)
; Function Attrs: nounwind
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @math.Trunc(double %x, i8* undef) #0
ret double %0
}
declare double @math.Trunc(double, i8*)
attributes #0 = { nounwind }
+37
View File
@@ -0,0 +1,37 @@
; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @llvm.sqrt.f64(double %x)
ret double %0
}
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare double @llvm.sqrt.f64(double) #1
; Function Attrs: nounwind
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @llvm.trunc.f64(double %x)
ret double %0
}
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare double @llvm.trunc.f64(double) #1
attributes #0 = { nounwind }
attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
+14
View File
@@ -0,0 +1,14 @@
package main
// Test how intrinsics are lowered: either as regular calls to the math
// functions or as LLVM builtins (such as llvm.sqrt.f64).
import "math"
func mySqrt(x float64) float64 {
return math.Sqrt(x)
}
func myTrunc(x float64) float64 {
return math.Trunc(x)
}
+37 -41
View File
@@ -3,80 +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 triple = "wasm32-unknown-wasi"
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; 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 #0 {
entry:
ret [0 x i32] zeroinitializer
}
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %x
%0 = bitcast i8* %x to i32*
call void @runtime.trackPointer(i8* %x, i8* undef) #0
ret i32* %0
}
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %x
%0 = bitcast i32* %x to i8*
call void @runtime.trackPointer(i8* %0, i8* undef) #0
ret i8* %0
}
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %x
call void @runtime.trackPointer(i8* %x, i8* undef) #0
ret i8* %x
}
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
%0 = getelementptr inbounds i8, ptr %ptr, i32 10
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %0
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
ret i8* %0
}
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
%0 = getelementptr inbounds i8, ptr %ptr, i32 %offset
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %0
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
call void @runtime.trackPointer(i8* %0, i8* undef) #0
call void @runtime.trackPointer(i8* %0, i8* undef) #0
ret i8* %0
}
; 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 #0 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
%0 = shl i32 %offset, 2
%1 = getelementptr inbounds i8, ptr %ptr, i32 %0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %1
%0 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %0, i8* undef) #0
%1 = getelementptr i32, i32* %ptr, i32 %offset
%2 = bitcast i32* %1 to i8*
call void @runtime.trackPointer(i8* %2, i8* undef) #0
%3 = bitcast i32* %1 to i8*
call void @runtime.trackPointer(i8* %3, i8* undef) #0
ret i32* %1
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
attributes #0 = { nounwind }
-10
View File
@@ -3,53 +3,44 @@ package main
import _ "unsafe"
// Creates an external global with name extern_global.
//
//go:extern extern_global
var externGlobal [0]byte
// Creates a
//
//go:align 32
var alignedGlobal [4]uint32
// Test conflicting pragmas (the last one counts).
//
//go:align 64
//go:align 16
var alignedGlobal16 [4]uint32
// Test exported functions.
//
//export extern_func
func externFunc() {
}
// Define a function in a different package using go:linkname.
//
//go:linkname withLinkageName1 somepkg.someFunction1
func withLinkageName1() {
}
// Import a function from a different package using go:linkname.
//
//go:linkname withLinkageName2 somepkg.someFunction2
func withLinkageName2()
// Function has an 'inline hint', similar to the inline keyword in C.
//
//go:inline
func inlineFunc() {
}
// Function should never be inlined, equivalent to GCC
// __attribute__((noinline)).
//
//go:noinline
func noinlineFunc() {
}
// This function should have the specified section.
//
//go:section .special_function_section
func functionInSection() {
}
@@ -60,7 +51,6 @@ func exportedFunctionInSection() {
}
// This function should not: it's only a declaration and not a definition.
//
//go:section .special_function_section
func undefinedFunctionNotInSection()
+16 -18
View File
@@ -6,64 +6,62 @@ target triple = "wasm32-unknown-wasi"
@extern_global = external global [0 x i8], align 1
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
@llvm.used = appending global [2 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection]
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
@undefinedGlobalNotInSection = external global i32, align 4
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define void @extern_func() #2 {
define void @extern_func() #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 {
define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #0 {
entry:
ret void
}
declare void @somepkg.someFunction2(ptr) #0
declare void @somepkg.someFunction2(i8*)
; Function Attrs: inlinehint nounwind
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 {
define hidden void @main.inlineFunc(i8* %context) unnamed_addr #2 {
entry:
ret void
}
; Function Attrs: noinline nounwind
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 {
define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #3 {
entry:
ret void
}
; 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 #0 section ".special_function_section" {
entry:
ret void
}
; Function Attrs: nounwind
define void @exportedFunctionInSection() #5 section ".special_function_section" {
define void @exportedFunctionInSection() #4 section ".special_function_section" {
entry:
ret void
}
declare void @main.undefinedFunctionNotInSection(ptr) #0
declare void @main.undefinedFunctionNotInSection(i8*)
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" }
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" }
attributes #0 = { nounwind }
attributes #1 = { nounwind "wasm-export-name"="extern_func" }
attributes #2 = { inlinehint nounwind }
attributes #3 = { noinline nounwind }
attributes #4 = { nounwind "wasm-export-name"="exportedFunctionInSection" }

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