mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cde28db20e |
@@ -0,0 +1,119 @@
|
||||
version: 2.1
|
||||
|
||||
commands:
|
||||
submodules:
|
||||
steps:
|
||||
- run:
|
||||
name: "Pull submodules"
|
||||
command: git submodule update --init
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-19-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-19-v1
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/compiler-rt
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
hack-ninja-jobs:
|
||||
steps:
|
||||
- run:
|
||||
name: "Hack Ninja to use less jobs"
|
||||
command: |
|
||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
||||
chmod +x /go/bin/ninja
|
||||
build-binaryen-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-linux-v3
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
make binaryen
|
||||
- save_cache:
|
||||
key: binaryen-linux-v3
|
||||
paths:
|
||||
- build/wasm-opt
|
||||
test-linux:
|
||||
parameters:
|
||||
llvm:
|
||||
type: string
|
||||
fmt-check:
|
||||
type: boolean
|
||||
default: true
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y \
|
||||
llvm-<<parameters.llvm>>-dev \
|
||||
clang-<<parameters.llvm>> \
|
||||
libclang-<<parameters.llvm>>-dev \
|
||||
lld-<<parameters.llvm>> \
|
||||
cmake \
|
||||
ninja-build
|
||||
- hack-ninja-jobs
|
||||
- build-binaryen-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v4-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- when:
|
||||
condition: <<parameters.fmt-check>>
|
||||
steps:
|
||||
- run:
|
||||
# Do this before gen-device so that it doesn't check the
|
||||
# formatting of generated files.
|
||||
name: Check Go code formatting
|
||||
command: make fmt-check lint
|
||||
- run: make gen-device -j4
|
||||
# TODO: change this to -skip='TestErrors|TestWasm' with Go 1.20
|
||||
- run: go test -tags=llvm<<parameters.llvm>> -short -run='TestBuild|TestTest|TestGetList|TestTraceback'
|
||||
- run: make smoketest XTENSA=0
|
||||
- save_cache:
|
||||
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-oldest:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
docker:
|
||||
- image: golang:1.22-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "15"
|
||||
resource_class: large
|
||||
test-newest:
|
||||
# This tests the latest supported LLVM version when linking against system
|
||||
# libraries.
|
||||
docker:
|
||||
- image: golang:1.25-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "20"
|
||||
resource_class: large
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-oldest
|
||||
# disable this test, since CircleCI seems unable to download due to rate-limits on Dockerhub.
|
||||
# - test-newest
|
||||
@@ -1,4 +1,5 @@
|
||||
build/
|
||||
llvm-*/
|
||||
.github
|
||||
.circleci
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -40,13 +40,13 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-${{ matrix.os }}-v1
|
||||
key: llvm-source-20-${{ matrix.os }}-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -68,10 +68,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-22-${{ matrix.os }}-v1
|
||||
key: llvm-build-20-${{ matrix.os }}-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
@@ -101,11 +101,16 @@ jobs:
|
||||
- name: Make release artifact
|
||||
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
# Note: this release artifact is double-zipped, see:
|
||||
# https://github.com/actions/upload-artifact/issues/39
|
||||
# We can essentially pick one of these:
|
||||
# - have a double-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@v4
|
||||
with:
|
||||
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||
archive: false
|
||||
- name: Smoke tests
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||
test-macos-homebrew:
|
||||
@@ -116,7 +121,7 @@ jobs:
|
||||
version: [16, 17, 18, 19, 20]
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@main
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Fix Python symlinks
|
||||
run: |
|
||||
# Github runners have broken symlinks, so relink
|
||||
@@ -125,13 +130,12 @@ jobs:
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
brew install llvm@${{ matrix.version }}
|
||||
brew link llvm@${{ matrix.version }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# This CI job checks whether at least the smoke tests pass for the oldest
|
||||
# Go/LLVM version we claim to support.
|
||||
|
||||
name: Version compatibility test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-compat:
|
||||
runs-on: ubuntu-22.04 # this must be a specific version for the apt install below
|
||||
env:
|
||||
# Oldest versions currently supported by TinyGo
|
||||
LLVM: "15"
|
||||
Go: "1.25" # when updating this, also update minorMin in builder/config.go
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ env.Go }}
|
||||
cache: true
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ env.LLVM }} main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y \
|
||||
llvm-${{ env.LLVM }}-dev \
|
||||
clang-${{ env.LLVM }} \
|
||||
libclang-${{ env.LLVM }}-dev \
|
||||
lld-${{ env.LLVM }} \
|
||||
binaryen
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-linux-compat
|
||||
path: llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: llvm-project/compiler-rt
|
||||
- name: Go cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: go-build-${{ env.Go }}-llvm${{ env.LLVM }}-${{ hashFiles('go.sum') }}
|
||||
- name: Build TinyGo
|
||||
run: go install -tags=llvm${{ env.LLVM }}
|
||||
- run: tinygo version
|
||||
- run: make gen-device -j4
|
||||
- run: go test -tags=llvm${{ env.LLVM }} -short -skip=TestErrors
|
||||
- run: make smoketest XTENSA=0
|
||||
@@ -31,14 +31,14 @@ jobs:
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
df -h
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
@@ -47,18 +47,18 @@ jobs:
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v4
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
+54
-84
@@ -12,36 +12,18 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
go-mod-tidy:
|
||||
# Check that go.sum is up to date.
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Run go mod tidy
|
||||
run: go mod tidy
|
||||
- name: Check go.mod and go.sum are up to date
|
||||
run: git diff --exit-code
|
||||
|
||||
build-linux:
|
||||
# Build Linux binaries, ready for release.
|
||||
# This runs inside an Alpine Linux container so we can more easily create a
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.27rc2-alpine
|
||||
image: golang:1.25-alpine
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Install apk dependencies
|
||||
# tar: needed for actions/cache@v5
|
||||
# tar: needed for actions/cache@v4
|
||||
# git+openssh: needed for checkout (I think?)
|
||||
# ruby: needed to install fpm
|
||||
run: apk add tar git openssh make g++ ruby-dev mold
|
||||
@@ -49,24 +31,24 @@ jobs:
|
||||
# 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@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-alpine-v2-${{ hashFiles('go.mod') }}
|
||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-linux-alpine-v1
|
||||
key: llvm-source-20-linux-alpine-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -77,7 +59,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -88,10 +70,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-22-linux-alpine-v1
|
||||
key: llvm-build-20-linux-alpine-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -106,16 +88,16 @@ jobs:
|
||||
# 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@v5
|
||||
uses: actions/cache/save@v4
|
||||
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@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-alpine-v3
|
||||
key: binaryen-linux-alpine-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
@@ -136,31 +118,26 @@ jobs:
|
||||
make release deb -j3 STATIC=1
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
- name: "Publish release artifact: tarball"
|
||||
uses: actions/upload-artifact@v7
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
path: /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
archive: false
|
||||
- name: "Publish release artifact: Debian package"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
path: /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
archive: false
|
||||
name: linux-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: |
|
||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
/tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
test-linux-build:
|
||||
# Test the binaries built in the build-linux job by running the smoke tests.
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
@@ -169,9 +146,9 @@ jobs:
|
||||
- name: Install wasm-tools
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
- name: Extract release tarball
|
||||
run: |
|
||||
mkdir -p ~/lib
|
||||
@@ -187,7 +164,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
@@ -204,12 +181,12 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
node-version: '18'
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
with:
|
||||
@@ -217,10 +194,10 @@ jobs:
|
||||
- name: Setup `wasm-tools`
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-linux-asserts-v1
|
||||
key: llvm-source-20-linux-asserts-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -231,7 +208,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -242,10 +219,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-22-linux-asserts-v1
|
||||
key: llvm-build-20-linux-asserts-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -258,13 +235,13 @@ jobs:
|
||||
# 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@v5
|
||||
uses: actions/cache/save@v4
|
||||
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@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-asserts-v1
|
||||
@@ -284,8 +261,6 @@ jobs:
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- run: make tinygo-test-baremetal
|
||||
- name: Check Go code formatting
|
||||
run: make fmt-check lint
|
||||
build-linux-cross:
|
||||
# Build ARM Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
@@ -309,7 +284,7 @@ jobs:
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Get TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
@@ -323,13 +298,13 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-linux-v1
|
||||
key: llvm-source-20-linux-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -340,7 +315,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -351,10 +326,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-22-linux-${{ matrix.goarch }}-v1
|
||||
key: llvm-build-20-linux-${{ matrix.goarch }}-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -369,16 +344,16 @@ jobs:
|
||||
# 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@v5
|
||||
uses: actions/cache/save@v4
|
||||
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@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v5
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v4
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
@@ -395,9 +370,9 @@ jobs:
|
||||
run: |
|
||||
make CROSS=${{ matrix.toolchain }}
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
@@ -409,17 +384,12 @@ jobs:
|
||||
- name: Create ${{ matrix.goarch }} release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||
- name: "Publish release artifact: tarball"
|
||||
uses: actions/upload-artifact@v7
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
path: /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
archive: false
|
||||
- name: "Publish release artifact: Debian package"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
path: /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||
archive: false
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: |
|
||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
/tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
||||
|
||||
@@ -25,14 +25,14 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/llvm-20
|
||||
@@ -46,13 +46,13 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v4
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
target: tinygo-llvm-build
|
||||
context: .
|
||||
|
||||
@@ -21,28 +21,28 @@ jobs:
|
||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||
run: sudo apt-get remove llvm-18
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Pull musl, bdwgc
|
||||
run: |
|
||||
git submodule update --init lib/musl lib/bdwgc
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-linux-nix-v1
|
||||
key: llvm-source-20-linux-nix-v1
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- name: Test
|
||||
run: |
|
||||
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
|
||||
|
||||
@@ -19,29 +19,25 @@ jobs:
|
||||
- name: Add GOBIN to $PATH
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '~1.25'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-sizediff-v1
|
||||
key: llvm-source-20-sizediff-v1
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
|
||||
@@ -17,14 +17,21 @@ jobs:
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -34,13 +41,13 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-windows-v3
|
||||
key: llvm-source-20-windows-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -51,7 +58,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save cached LLVM source
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -62,10 +69,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore cached LLVM build
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-22-windows-v1
|
||||
key: llvm-build-20-windows-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -79,21 +86,20 @@ jobs:
|
||||
# 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@v5
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Go cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-windows-v3-${{ hashFiles('go.mod') }}
|
||||
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
C:/Users/runneradmin/AppData/Local/go-build
|
||||
C:/Users/runneradmin/go/pkg/mod
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install wasmtime@29.0.1
|
||||
- name: make gen-device
|
||||
run: make -j3 gen-device
|
||||
@@ -108,35 +114,50 @@ jobs:
|
||||
working-directory: build/release
|
||||
run: 7z -tzip a tinygo${{ steps.version.outputs.version }}.windows-amd64.zip tinygo
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
# Note: this release artifact is double-zipped, see:
|
||||
# https://github.com/actions/upload-artifact/issues/39
|
||||
# We can essentially pick one of these:
|
||||
# - 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@v4
|
||||
with:
|
||||
name: tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||
archive: false
|
||||
|
||||
smoke-test-windows:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
# This build is already unzipped.
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
@@ -145,19 +166,28 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
# This build is already unzipped.
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
@@ -165,24 +195,34 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install binaryen wasmtime@29.0.1
|
||||
scoop install binaryen && scoop install wasmtime@29.0.1
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.25.5'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
# This build is already unzipped.
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Test stdlib packages on wasip1
|
||||
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
-157
@@ -1,160 +1,3 @@
|
||||
0.41.1
|
||||
---
|
||||
* **machine**
|
||||
- esp32c3: correct pin interrupt setup call that was overlooked from #5320
|
||||
* **runtime**
|
||||
- esp32s3: wait for TIMG0 update register to clear before reading timer registers
|
||||
* **net**
|
||||
- update net module to a version that is backwards compatible with Go 1.25.x to fix #5332
|
||||
|
||||
0.41.0
|
||||
---
|
||||
* **general**
|
||||
- go.mod, compiler: bump minimum Go version to 1.23
|
||||
- builder: support Go 1.26 now
|
||||
- feat: add inheritable-only field to filter processor-level targets (#5270)
|
||||
- feature: add esp32flash flash method for esp32s3/esp32c3/esp32/esp8266
|
||||
- flashing: introduce flash method 'esp32jtag' for esp32c3/esp32s3 targets
|
||||
- fashing: add "adb" flash method using Android Debug Bridge
|
||||
- main: auto-use best available flashing options on esp32
|
||||
- espflasher: update to espflasher 0.6.0
|
||||
* **compiler**
|
||||
- implement method-set based AssignableTo and Implements (#5304)
|
||||
- simplify createObjectLayout
|
||||
- implement copy directly
|
||||
- fix min/max on floats by using intrinsics
|
||||
- add element layout to sliceAppend
|
||||
- add intrinsic for crypto/internal/constanttime.boolToUint8
|
||||
- fix SyscallN handling for Windows on Go 1.26+
|
||||
* **core**
|
||||
- reflect: add TypeAssert
|
||||
- reflect: fix TypeAssert for structs
|
||||
- sync: add WaitGroup.Go
|
||||
- os: add UserCacheDir and UserConfigDir
|
||||
- os: implement Lchown function for changing file ownership (#5161)
|
||||
- testing: add b.ReportMetric()
|
||||
- errors: add errors package to passing list
|
||||
- internal/gclayout: use correct lengths
|
||||
- internal/abi: add EscapeNonString, EscapeToResultNonString
|
||||
- internal, runtime: add internal fips bool
|
||||
- internal/itoa: resurrect from Go stdlib
|
||||
- internal/syscall/unix: implement GetRandom for WASI targets
|
||||
- interp: make ptrtoint size mismatch a recoverable error
|
||||
- loader: support "all:" prefix in //go:embed patterns
|
||||
- loader, crypto/internal/entropy: fix 32 MiB RAM overflow on microcontrollers
|
||||
- loader, unicode/utf8: fix hiBits overflow on 16-bit AVR targets
|
||||
- builder: order embedded files deterministically
|
||||
- builder: fix panic in findPackagePath when using -size short
|
||||
- builder: fix SIGSEGV when stripping duplicate function definitions
|
||||
- builder, runtime: fix duplicate symbol error with Go 1.26+ on Windows
|
||||
- builder: use target-specific -march for RISC-V library compilation
|
||||
- transform: modify output format from the -print-allocs flag to match the go coverage tool format
|
||||
- cgo: allow for changes to 'short-enums' flag
|
||||
- wasm: add more stubbed file operations
|
||||
* **machine**
|
||||
- esp32c3: implement BlockDevice for esp32c3 flash
|
||||
- esp32c3: clear GPIO STATUS before dispatching pin callbacks
|
||||
- esp32c3: implement USBDevice using interrupts
|
||||
- esp32c3: add Enable stub for QEMU test target
|
||||
- esp32c3: fix interrupt disable handling
|
||||
- esp32c3: clear MCAUSE after handling interrupt
|
||||
- esp32c3: map missing IRAM sections in linker script
|
||||
- esp32c3: fix to use PROVIDE() for WiFi/BLE ROM function addresses to allow blob overrides
|
||||
- esp32c3: add WiFi/BLE ROM linker support
|
||||
- esp32c3: use TIMG0 alarm interrupt for sleepTicks instead of busy-waiting
|
||||
- esp32s3: implement Pin.SetInterrupt for GPIO pin change interrupts
|
||||
- esp32s3: use edge-triggered CPU interrupt for GPIO pin interrupts
|
||||
- esp32s3: add interrupt support (#5244)
|
||||
- esp32s3: switch USB implementation to use interrupts instead of polling
|
||||
- esp32s3: replace inline ISR with full interrupt vector handler
|
||||
- esp32s3: use TIMG0 alarm interrupt for sleepTicks
|
||||
- esp32s3: improve exception handlers, add procPin/procUnpin, and linker wrap flags
|
||||
- esp32s3: fix Xtensa register window spill using recursive call4 in task switching
|
||||
- esp32s3: update linker script and boot assembly for multi-page flash XIP mapping
|
||||
- esp32s3: add flash XIP boot assembly with cache/MMU init
|
||||
- esp32s3: implement SPI (#5169)
|
||||
- esp32s3: implement I2C interface (#5210)
|
||||
- esp32s3: implement RNG based on onboard hardware random number generator
|
||||
- esp32s3,esp32c3: add USB serial support
|
||||
- esp32s3,esp32c3: add txStalled flag to skip USB serial spin when no host
|
||||
- esp32s3,esp32c3: make USB Serial/JTAG writes non-blocking when FIFO is full
|
||||
- esp32c3/esp32s3: refactor ADC implementation to reduce code duplication
|
||||
- esp32s3,esp32c3: add ADC support (#5231)
|
||||
- esp32s3,esp32c3: add PWM support (#5215)
|
||||
- esp32c3/esp32s3: refactoring and corrections for SPI implementation
|
||||
- esp32xx: add WASM simulator support for ESP32-C3/ESP32-S3 targets
|
||||
- esp32: default SPI CS pin to NoPin when unset
|
||||
- esp: fix tinygo_scanCurrentStack to spill register windows
|
||||
- stm32: fix UART interrupt storm caused by uncleared overrun error
|
||||
- stm32: fix PWM problem due to register shifting
|
||||
- stm32: add STM32U585 target definition and runtime
|
||||
- stm32: add STM32U5 GPIO, pin interrupts, and UART support
|
||||
- stm32: add STM32U5 I2C support
|
||||
- stm32: add STM32U5 SPI support
|
||||
- stm32u585: implement ADC
|
||||
- stm32g0b1: add support (#5150)
|
||||
- stm32g0b1: add Watchdog + ADC support (#5158)
|
||||
- stm32l0: add flash support
|
||||
- stm32l0x1,l0x2: TIM: adapt to 32-bit register access
|
||||
- stm32g0: sync with updated stm32 device files
|
||||
- attiny85: add USI-based SPI support (#5181)
|
||||
- attiny85: add PWM support (#5171)
|
||||
- rp: add Close function to UART to allow for removing all system resources/power usage
|
||||
- rp: use blockReset() and unresetBlockWait() helper functions for peripheral reset/unreset
|
||||
- rp2: prevent PWM Period method overflow
|
||||
- rp2: add per-byte timeout budget for I2C (#5189)
|
||||
- feather-m0: export UART0 and pins
|
||||
- usb/cdc: better ring buffer implementation (#5209)
|
||||
- fix: replace ! with ~ for register mask
|
||||
- atmega328p_simulator: add correct build tag for arduino_uno after renaming
|
||||
* **net**
|
||||
- update to Go 1.26.2 net package with UDP and JS improvements
|
||||
* **runtime**
|
||||
- implement fminimum/fmaximum
|
||||
- make timeoffset atomic
|
||||
- add MemStats.HeapObjects
|
||||
- handle GODEBUG on wasip2 (#5312)
|
||||
- fix Darwin syscall return value truncation and lost arguments
|
||||
- add syscall.runtimeClearenv for Go 1.26
|
||||
- split syscall functions for darwin changes for 1.26
|
||||
- fix: init heap before random number seed on wasm platforms
|
||||
* **targets**
|
||||
- add esp32s3-supermini board target
|
||||
- add support for Seeedstudio Xiao-RP2350 board
|
||||
- add Shrike Lite board (#5170)
|
||||
- rename arduino target to arduino-uno
|
||||
- add pins/setup for Arduino UNO Q board QWIIC connector
|
||||
- correct pin mapping for Arduino UNO Q STM32 MCU
|
||||
- add stm32u585 openocd commands for flashing on Arduino UNO Q
|
||||
- correct name/tag use for esp32s3-wroom1 board
|
||||
- modify esp32, esp32s3, esp32c3, & esp8266 targets to use built-in esp32flash
|
||||
- switch rp2040/rp2350 to default to tasks scheduler
|
||||
- change default stack size for esp32c3 and esp32s3 to 8196
|
||||
- swan: rename group build tag stm32l4x5 => stm32l4y5 to avoid clash with stm32 device name
|
||||
- nucleo-f722ze: add build-tag stm32f722
|
||||
- fix: set stm32u5x clock rate to default
|
||||
- create generic targets for esp32 boards
|
||||
* **libs**
|
||||
- stm32-svd: update submodule
|
||||
- update tools/gen-device-svd for recent changes to stm32-svd file structure (#5212)
|
||||
* **build/test**
|
||||
- build: use Golang 1.26 for all builds
|
||||
- build: actually use the version of llvm we are supposed to be testing by using brew link command
|
||||
- build: let scoop run updates to resolve binaryen install failures on Windows
|
||||
- ci: don't double zip release artifacts
|
||||
- test: increase default timeout from 1 to 2 minutes to prevent spurious CI fails
|
||||
- testdata: fix flaky timers test by draining ticker channel after Stop
|
||||
- testdata: more corpus entries (#5182)
|
||||
- add soypat/lneto to corpus
|
||||
- GNUmakefile: add context and expvar to stdlib tests
|
||||
- GNUmakefile: add encoding/xml to stdlib tests on Linux
|
||||
- main (test): skip mipsle test if the emulator is missing
|
||||
- make: remove machine without board from smoketest
|
||||
- stm32: add Arduino UNO Q to smoketests
|
||||
- flake: bump to nixpkgs 25.11
|
||||
- chore: update all github actions to latest versions
|
||||
- fix: use external 7zip for scoop installs on Windows
|
||||
|
||||
0.40.1
|
||||
---
|
||||
* **machine**
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.27rc2 AS tinygo-llvm
|
||||
FROM golang:1.25 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
||||
@@ -33,7 +33,7 @@ RUN cd /tinygo/ && \
|
||||
|
||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||
# all the build tools etc).
|
||||
FROM golang:1.27rc2 AS tinygo-compiler
|
||||
FROM golang:1.25 AS tinygo-compiler
|
||||
|
||||
# Copy tinygo build.
|
||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||
|
||||
+32
-147
@@ -133,7 +133,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 dtlto executionengine frontenddriver frontendhlsl 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 frontenddriver frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE = .exe
|
||||
@@ -142,9 +142,6 @@ ifeq ($(OS),Windows_NT)
|
||||
|
||||
# PIC needs to be disabled for libclang to work.
|
||||
LLVM_OPTION += -DLLVM_ENABLE_PIC=OFF
|
||||
# Statically link the C++ and GCC runtime into LLVM tools so they don't
|
||||
# depend on MinGW DLLs that may not be on PATH when executed during the build.
|
||||
LLVM_OPTION += '-DCMAKE_EXE_LINKER_FLAGS=-static-libgcc -static-libstdc++'
|
||||
|
||||
CGO_CPPFLAGS += -DCINDEX_NO_EXPORTS
|
||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
@@ -172,7 +169,7 @@ endif
|
||||
MD5SUM ?= md5sum
|
||||
|
||||
# Libraries that should be linked in for the statically linked Clang.
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAnalysisLifetimeSafety clangAPINotes clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangInstallAPI clangLex clangOptions clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAPINotes clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangInstallAPI clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport 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.
|
||||
@@ -180,7 +177,7 @@ LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
|
||||
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
||||
|
||||
# Other libraries that are needed to link TinyGo.
|
||||
EXTRA_LIB_NAMES = LLVMDTLTO LLVMInterpreter LLVMMCA LLVMRISCVTargetMCA LLVMX86TargetMCA
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMRISCVTargetMCA LLVMX86TargetMCA
|
||||
|
||||
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
|
||||
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
||||
@@ -197,12 +194,6 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm lld $(addprefix lib/lib,
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++17
|
||||
ifneq ($(uname),Windows_NT)
|
||||
# Disable GCC DWARF compression: lld built without zlib cannot link
|
||||
# object files with ELFCOMPRESS_ZLIB debug sections.
|
||||
CGO_CFLAGS+=-gz=none
|
||||
CGO_CXXFLAGS+=-gz=none
|
||||
endif
|
||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
|
||||
@@ -272,7 +263,7 @@ gen-device-renesas: build/gen-device-svd
|
||||
GO111MODULE=off $(GO) fmt ./src/device/renesas
|
||||
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b tinygo_22.x --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
git clone -b tinygo_20.x --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm ## Get LLVM sources
|
||||
|
||||
# Configure LLVM.
|
||||
@@ -298,16 +289,16 @@ WASM_TOOLS_MODULE=go.bytecodealliance.org
|
||||
.PHONY: wasi-syscall
|
||||
wasi-syscall: wasi-cm
|
||||
rm -rf ./src/internal/wasi/*
|
||||
go run $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
|
||||
go run -modfile ./internal/wasm-tools/go.mod $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
|
||||
|
||||
# Copy package cm into src/internal/cm
|
||||
.PHONY: wasi-cm
|
||||
wasi-cm:
|
||||
rm -rf ./src/internal/cm/*
|
||||
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
|
||||
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -modfile ./internal/wasm-tools/go.mod -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
|
||||
|
||||
# Check for Node.js used during WASM tests.
|
||||
MIN_NODEJS_VERSION=22
|
||||
MIN_NODEJS_VERSION=18
|
||||
|
||||
.PHONY: check-nodejs-version
|
||||
check-nodejs-version:
|
||||
@@ -319,9 +310,9 @@ check-nodejs-version:
|
||||
|
||||
tinygo: ## Build the TinyGo compiler
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm llvm22 osusergo" .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
|
||||
test: check-nodejs-version
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22 osusergo" $(GOTESTPKGS)
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
|
||||
|
||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||
TEST_PACKAGES_SLOW = \
|
||||
@@ -348,17 +339,14 @@ TEST_PACKAGES_FAST = \
|
||||
embed/internal/embedtest \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
errors \
|
||||
encoding/asn1 \
|
||||
encoding/base32 \
|
||||
encoding/base64 \
|
||||
encoding/csv \
|
||||
encoding/hex \
|
||||
expvar \
|
||||
go/ast \
|
||||
go/format \
|
||||
go/scanner \
|
||||
go/token \
|
||||
go/version \
|
||||
hash \
|
||||
hash/adler32 \
|
||||
@@ -371,7 +359,6 @@ TEST_PACKAGES_FAST = \
|
||||
math/cmplx \
|
||||
net/http/internal/ascii \
|
||||
net/mail \
|
||||
net/url \
|
||||
os \
|
||||
path \
|
||||
reflect \
|
||||
@@ -388,36 +375,32 @@ TEST_PACKAGES_FAST = \
|
||||
# archive/zip requires os.ReadAt, which is not yet supported on windows
|
||||
# bytes requires mmap
|
||||
# compress/flate appears to hang on wasi
|
||||
# crypto/aes needs reflect.Type.Method(), not yet implemented
|
||||
# crypto/aes fails on wasi, needs panic()/recover()
|
||||
# crypto/des fails on wasi, needs panic()/recover()
|
||||
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
|
||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||
# encoding/xml takes a minute on linux and gives a stack overflow on wasi
|
||||
# image fails on wasi, needs panic()/recover()
|
||||
# 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: fails on wasi, needs panic()/recover()
|
||||
# mime: fail on wasi; neds panic()/recover()
|
||||
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# mime/quotedprintable requires syscall.Faccessat
|
||||
# net/mail: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# net/ntextproto: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# regexp/syntax: fails on wasip1, needs panic()/recover()
|
||||
# strconv: fails on wasi, needs panic()/recover()
|
||||
# text/tabwriter: fails on wasi, needs panic()/recover()
|
||||
# text/template/parse: fails on wasi, needs panic()/recover()
|
||||
# regexp/syntax: fails on wasip1; needs panic()/recover()
|
||||
# strconv requires recover() which is not yet supported on wasi
|
||||
# text/tabwriter requires recover(), which is not yet supported on wasi
|
||||
# text/template/parse requires recover(), which is not yet supported on wasi
|
||||
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
|
||||
# Additional standard library packages that pass tests on individual platforms
|
||||
TEST_PACKAGES_LINUX := \
|
||||
archive/zip \
|
||||
compress/flate \
|
||||
context \
|
||||
crypto/aes \
|
||||
crypto/des \
|
||||
crypto/ecdh \
|
||||
crypto/hmac \
|
||||
debug/dwarf \
|
||||
debug/plan9obj \
|
||||
encoding/xml \
|
||||
image \
|
||||
io/ioutil \
|
||||
mime \
|
||||
@@ -429,7 +412,6 @@ TEST_PACKAGES_LINUX := \
|
||||
os/user \
|
||||
regexp/syntax \
|
||||
strconv \
|
||||
testing/fstest \
|
||||
text/tabwriter \
|
||||
text/template/parse
|
||||
|
||||
@@ -440,11 +422,7 @@ TEST_PACKAGES_WINDOWS := \
|
||||
compress/flate \
|
||||
crypto/des \
|
||||
crypto/hmac \
|
||||
image \
|
||||
mime \
|
||||
regexp/syntax \
|
||||
strconv \
|
||||
text/tabwriter \
|
||||
text/template/parse \
|
||||
$(nil)
|
||||
|
||||
@@ -459,7 +437,6 @@ TEST_PACKAGES_NONWASM = \
|
||||
crypto/ecdsa \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
expvar \
|
||||
go/format \
|
||||
os \
|
||||
testing \
|
||||
@@ -479,11 +456,6 @@ TEST_PACKAGES_NONBAREMETAL = \
|
||||
math \
|
||||
$(nil)
|
||||
|
||||
TEST_PACKAGES_FAST_WASI = $(filter-out $(TEST_PACKAGES_NOWASI), $(TEST_PACKAGES_FAST))
|
||||
TEST_PACKAGES_NOWASI = \
|
||||
crypto/ecdsa \
|
||||
$(nil)
|
||||
|
||||
# Report platforms on which each standard library package is known to pass tests
|
||||
report-stdlib-tests-pass:
|
||||
$(eval jointmp := $(shell echo /tmp/join.$$$$))
|
||||
@@ -498,19 +470,17 @@ report-stdlib-tests-pass:
|
||||
ifeq ($(uname),Darwin)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||
TEST_IOFS := true
|
||||
TEST_ENCODING_XML := true
|
||||
endif
|
||||
ifeq ($(uname),Linux)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||
TEST_IOFS := true
|
||||
TEST_ENCODING_XML := true
|
||||
endif
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation'
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
||||
TEST_ADDITIONAL_FLAGS ?=
|
||||
|
||||
# Test known-working standard library packages.
|
||||
@@ -518,11 +488,8 @@ TEST_ADDITIONAL_FLAGS ?=
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||
@# TestParseAndBytesRoundTrip/P256/Generic: needs Goexit to run defers on wasm.
|
||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml,$(TEST_PACKAGES_HOST)) $(TEST_PACKAGES_SLOW)
|
||||
ifeq ($(TEST_ENCODING_XML),true)
|
||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -stack-size=16MB encoding/xml
|
||||
endif
|
||||
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
|
||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
@# 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.
|
||||
@@ -544,12 +511,12 @@ tinygo-test-wasi:
|
||||
tinygo-test-wasip1:
|
||||
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
tinygo-test-wasip1-fast:
|
||||
$(TINYGO) test -target=wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASI) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target=wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-slow:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-wasip2-fast:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASI) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-sum-slow:
|
||||
TINYGO=$(TINYGO) \
|
||||
@@ -579,13 +546,13 @@ tinygo-test-baremetal:
|
||||
|
||||
# Test external packages in a large corpus.
|
||||
test-corpus:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -run TestCorpus . -corpus=testdata/corpus.yaml
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml
|
||||
test-corpus-fast:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -run TestCorpus -short . -corpus=testdata/corpus.yaml
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus -short . -corpus=testdata/corpus.yaml
|
||||
test-corpus-wasi:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasip1
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasip1
|
||||
test-corpus-wasip2:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasip2
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasip2
|
||||
|
||||
.PHONY: testchdir
|
||||
testchdir:
|
||||
@@ -657,11 +624,9 @@ smoketest: testchdir
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.efi -target=uefi-amd64 examples/test
|
||||
@$(MD5SUM) test.efi
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino_uno examples/blinky1
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
@@ -679,8 +644,6 @@ ifneq ($(WASM), 0)
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=pico examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=xiao_esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
endif
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
@@ -769,8 +732,6 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao-ble-plus examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=rak4631 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/dac
|
||||
@@ -807,10 +768,6 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico-w examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/blinky1
|
||||
@@ -849,24 +806,14 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2-w examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=tiny2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=badger2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=blinky2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico-plus2 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=metro-rp2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=vicharak_shrike-lite examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao-rp2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -898,8 +845,6 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-h753zi examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l031k6 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
||||
@@ -928,26 +873,18 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32l0x1 examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32u031 examples/empty
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinkm
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno examples/blinky1
|
||||
$(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-uno examples/pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno -scheduler=tasks examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -959,79 +896,28 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/mcp3008
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stamp-s3a examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stick-c examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5paper examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32c6
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c6 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c6 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32s3
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
# esp32s3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-box-3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
# esp32c3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/machinetest
|
||||
@@ -1044,7 +930,6 @@ endif
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=makerfabs-esp32c3spi35 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
@@ -1215,8 +1100,8 @@ endif
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/unistd build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/sources build/release/tinygo/lib/wasi-libc/libc-top-half
|
||||
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
|
||||
@cp -rp ${LLVM_PROJECTDIR}/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp ${LLVM_PROJECTDIR}/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp src build/release/tinygo/src
|
||||
@cp -rp targets build/release/tinygo/targets
|
||||
|
||||
@@ -1238,7 +1123,7 @@ endif
|
||||
|
||||
.PHONY: tools
|
||||
tools:
|
||||
go generate -tags tools ./
|
||||
cd internal/tools && go generate -tags tools ./
|
||||
|
||||
LINTDIRS=src/os/ src/reflect/
|
||||
.PHONY: lint
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
# TinyGo - Go compiler for small places
|
||||
|
||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml)
|
||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
||||
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
||||
|
||||
It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> You can help TinyGo with a financial contribution using OpenCollective. Please see https://opencollective.com/tinygo for more information. Thank you!
|
||||
|
||||
## Embedded
|
||||
|
||||
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
||||
@@ -34,10 +31,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
The above program can be compiled and run without modification on an [Arduino Uno](https://tinygo.org/docs/reference/microcontrollers/boards/arduino-uno), an [Adafruit Circuit Playground Express](https://tinygo.org/docs/reference/microcontrollers/featured/circuitplay-express), a [Seeed Studio XIAO-ESP32S3](https://tinygo.org/docs/reference/microcontrollers/featured/xiao-esp32s3) or any of the many supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||
The above program can be compiled and run without modification on an Arduino Uno, an Adafruit ItsyBitsy M0, or any of the supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||
|
||||
```shell
|
||||
tinygo flash -target arduino-uno examples/blinky1
|
||||
tinygo flash -target arduino examples/blinky1
|
||||
```
|
||||
|
||||
## WebAssembly
|
||||
@@ -66,7 +63,7 @@ tinygo build -buildmode=c-shared -o add.wasm -target=wasip1 add.go
|
||||
You can also use the same syntax as Go 1.24+:
|
||||
|
||||
```shell
|
||||
GOOS=wasip1 GOARCH=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
|
||||
GOARCH=wasip1 GOOS=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
|
||||
```
|
||||
|
||||
## Installation
|
||||
@@ -77,7 +74,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
### Embedded
|
||||
|
||||
You can compile TinyGo programs for over 150 different microcontroller boards.
|
||||
You can compile TinyGo programs for over 94 different microcontroller boards.
|
||||
|
||||
For more information, please see https://tinygo.org/docs/reference/microcontrollers/
|
||||
|
||||
@@ -142,7 +139,7 @@ Non-goals:
|
||||
|
||||
## Why this project exists
|
||||
|
||||
> We never expected Go to be an embedded language, and so it’s got serious problems...
|
||||
> We never expected Go to be an embedded language and so its got serious problems...
|
||||
|
||||
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
|
||||
|
||||
|
||||
+17
-65
@@ -14,13 +14,11 @@ import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"hash/crc32"
|
||||
"maps"
|
||||
"math/bits"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -138,7 +136,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if _, ok := globalValues[pkgPath]; !ok {
|
||||
globalValues[pkgPath] = map[string]string{}
|
||||
}
|
||||
maps.Copy(globalValues[pkgPath], vals)
|
||||
for k, v := range vals {
|
||||
globalValues[pkgPath][k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a libc dependency.
|
||||
@@ -254,18 +254,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||
}
|
||||
|
||||
// Strip default initializers for -X globals from the type info before
|
||||
// building SSA. This prevents go/ssa from emitting init stores for them,
|
||||
// so that makeGlobalsModule can supply the correct values at final link
|
||||
// time without any runtime init overwriting them. The -X values themselves
|
||||
// are kept out of the per-package build cache; only the variable names
|
||||
// appear in the cache key.
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
for name := range globalValues[pkg.Pkg.Path()] {
|
||||
pkg.StripVarInitializer(name)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||
// program so it's pretty fast and doesn't need to be parallelized.
|
||||
program := lprogram.LoadSSA()
|
||||
@@ -277,6 +265,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
var embedFileObjects []*compileJob
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg // necessary to avoid a race condition
|
||||
|
||||
var undefinedGlobals []string
|
||||
for name := range globalValues[pkg.Pkg.Path()] {
|
||||
@@ -292,13 +281,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||
}
|
||||
}
|
||||
// Sort embedded files by name to maintain output determinism.
|
||||
embedNames := make([]string, 0, len(allFiles))
|
||||
for _, files := range allFiles {
|
||||
embedNames = append(embedNames, files[0].Name)
|
||||
}
|
||||
slices.Sort(embedNames)
|
||||
for _, name := range embedNames {
|
||||
for name, files := range allFiles {
|
||||
name := name
|
||||
files := files
|
||||
job := &compileJob{
|
||||
description: "make object file for " + name,
|
||||
run: func(job *compileJob) error {
|
||||
@@ -313,7 +298,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
sum := sha256.Sum256(data)
|
||||
hexSum := hex.EncodeToString(sum[:16])
|
||||
|
||||
for _, file := range allFiles[name] {
|
||||
for _, file := range files {
|
||||
file.Size = uint64(len(data))
|
||||
file.Hash = hexSum
|
||||
if file.NeedsData {
|
||||
@@ -487,7 +472,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.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -556,23 +541,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||
}
|
||||
// Resolve duplicate function definitions before linking.
|
||||
// This can happen when a newer Go version adds a function
|
||||
// body in a standard library package that was previously
|
||||
// just a declaration provided by //go:linkname from the
|
||||
// runtime. In that case, keep the existing (runtime)
|
||||
// definition by weakening the new one's linkage so the
|
||||
// LLVM linker discards it in favor of the existing one.
|
||||
for fn := pkgMod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
existing := mod.NamedFunction(fn.Name())
|
||||
if existing.IsNil() || existing.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
}
|
||||
err = llvm.LinkModules(mod, pkgMod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link module: %w", err)
|
||||
@@ -773,6 +741,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
// TODO: do this as part of building the package to be able to link the
|
||||
// bitcode files together.
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg
|
||||
for _, filename := range pkg.CFiles {
|
||||
abspath := filepath.Join(pkg.OriginalDir(), filename)
|
||||
job := &compileJob{
|
||||
@@ -839,25 +808,22 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
}
|
||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
|
||||
switch config.LinkerFlavor() {
|
||||
case "coff":
|
||||
// Options for driving ld.lld in PE/COFF mode.
|
||||
if config.GOOS() == "windows" {
|
||||
// Options for the MinGW wrapper for the lld COFF linker.
|
||||
ldflags = append(ldflags,
|
||||
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||
case "darwin":
|
||||
} else if config.GOOS() == "darwin" {
|
||||
// Options for the ld64-compatible lld linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(speedLevel),
|
||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||
case "gnu":
|
||||
} else {
|
||||
// Options for the ELF linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(speedLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||
)
|
||||
default:
|
||||
return fmt.Errorf("unknown linker flavor: %s", config.LinkerFlavor())
|
||||
}
|
||||
if config.CodeModel() != "default" {
|
||||
ldflags = append(ldflags,
|
||||
@@ -914,7 +880,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
}
|
||||
|
||||
// Run wasm-opt for wasm binaries
|
||||
if arch, _, _ := strings.Cut(config.Triple(), "-"); arch == "wasm32" {
|
||||
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
||||
optLevel, _, _ := config.OptLevel()
|
||||
opt := "-" + optLevel
|
||||
|
||||
@@ -1076,7 +1042,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp32c6", "esp8266":
|
||||
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp8266":
|
||||
// Special format for the ESP family of chips (parsed by the ROM
|
||||
// bootloader).
|
||||
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||
@@ -1208,7 +1174,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
||||
// needed to convert a program to its final form. Some transformations are not
|
||||
// optional and must be run as the compiler expects them to run.
|
||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1352,14 +1318,6 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
}
|
||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
||||
|
||||
// Account for the bytes that tinygo_swapTask pushes onto the goroutine stack
|
||||
// on every context switch. The static analysis correctly traces Go calls,
|
||||
// but it cannot see into the assembly-level register push.
|
||||
var contextSwitchOverhead uint64
|
||||
if swapFuncs, ok := functions["tinygo_swapTask"]; ok && len(swapFuncs) == 1 {
|
||||
contextSwitchOverhead = swapFuncs[0].FrameSize
|
||||
}
|
||||
|
||||
sizes := make(map[string]functionStackSize)
|
||||
|
||||
// Add the reset handler function, for convenience. The reset handler runs
|
||||
@@ -1408,12 +1366,6 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
// overflow will occur even before the goroutine is started.
|
||||
stackSize = baseStackSize
|
||||
}
|
||||
if stackSizeType == stacksize.Bounded {
|
||||
// Add the overhead of context switching. This is needed because the
|
||||
// context switch (tinygo_swapTask) pushes callee-saved registers
|
||||
// onto the current stack, which is not seen by the static analysis.
|
||||
stackSize += contextSwitchOverhead
|
||||
}
|
||||
sizes[name] = functionStackSize{
|
||||
stackSize: stackSize,
|
||||
stackSizeType: stackSizeType,
|
||||
|
||||
+4
-62
@@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -29,7 +28,6 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32s3",
|
||||
"fe310",
|
||||
"gameboy-advance",
|
||||
@@ -37,7 +35,6 @@ func TestClangAttributes(t *testing.T) {
|
||||
"nintendoswitch",
|
||||
"riscv-qemu",
|
||||
"tkey",
|
||||
"uefi-amd64",
|
||||
"wasip1",
|
||||
"wasip2",
|
||||
"wasm",
|
||||
@@ -49,6 +46,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
targetNames = append(targetNames, "esp32", "esp8266")
|
||||
}
|
||||
for _, targetName := range targetNames {
|
||||
targetName := targetName
|
||||
t.Run(targetName, func(t *testing.T) {
|
||||
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
||||
})
|
||||
@@ -130,10 +128,8 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
defer mod.Dispose()
|
||||
|
||||
// Check whether the LLVM target matches.
|
||||
// Use ClangTriple since LLVM 22 normalizes wasm32-unknown-wasi to wasip1.
|
||||
expectedTriple := compileopts.ClangTriple(config.Triple())
|
||||
if mod.Target() != expectedTriple {
|
||||
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", expectedTriple, mod.Target())
|
||||
if mod.Target() != config.Triple() {
|
||||
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
|
||||
}
|
||||
|
||||
// Check the "target-cpu" and "target-features" string attribute of the add
|
||||
@@ -157,65 +153,11 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
// The reason is that Debian has patched Clang in a way that
|
||||
// modifies the LLVM features string, changing lots of FPU/float
|
||||
// related flags. We want to test vanilla Clang, not Debian Clang.
|
||||
//
|
||||
// Rather than requiring an exact match (which breaks across LLVM
|
||||
// versions as new features are added), check that every feature
|
||||
// TinyGo specifies is consistent with what Clang produces:
|
||||
// - A "+feature" in TinyGo must appear in Clang's output.
|
||||
// - A "-feature" in TinyGo must not be "+feature" in Clang's output.
|
||||
checkFeatureFlags(t, config.Features(), features)
|
||||
t.Errorf("target has LLVM features\n\t%#v\nbut Clang makes it\n\t%#v", config.Features(), features)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkFeatureFlags verifies that all features specified by TinyGo's target
|
||||
// configuration are consistent with Clang's output. This allows Clang to add
|
||||
// new features across LLVM versions without breaking the test.
|
||||
func checkFeatureFlags(t *testing.T, targetFeatures, clangFeatures string) {
|
||||
t.Helper()
|
||||
|
||||
// Build a set of Clang's features for fast lookup.
|
||||
clangSet := make(map[string]bool) // feature name -> enabled
|
||||
for _, f := range strings.Split(clangFeatures, ",") {
|
||||
f = strings.TrimSpace(f)
|
||||
if len(f) < 2 {
|
||||
continue
|
||||
}
|
||||
enabled := f[0] == '+'
|
||||
name := f[1:]
|
||||
clangSet[name] = enabled
|
||||
}
|
||||
|
||||
// Check each feature that TinyGo specifies.
|
||||
var missing, conflicts []string
|
||||
for _, f := range strings.Split(targetFeatures, ",") {
|
||||
f = strings.TrimSpace(f)
|
||||
if len(f) < 2 {
|
||||
continue
|
||||
}
|
||||
wantEnabled := f[0] == '+'
|
||||
name := f[1:]
|
||||
|
||||
clangEnabled, inClang := clangSet[name]
|
||||
if wantEnabled && (!inClang || !clangEnabled) {
|
||||
// TinyGo requires +feature but Clang doesn't enable it.
|
||||
missing = append(missing, f)
|
||||
} else if !wantEnabled && inClang && clangEnabled {
|
||||
// TinyGo requires -feature but Clang enables it.
|
||||
conflicts = append(conflicts, fmt.Sprintf("target has %q but Clang has %q", f, "+"+name))
|
||||
}
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
t.Errorf("target specifies features not present in Clang output: %s\n\ttarget features: %s\n\tclang features: %s",
|
||||
strings.Join(missing, ", "), targetFeatures, clangFeatures)
|
||||
}
|
||||
if len(conflicts) > 0 {
|
||||
t.Errorf("target disables features that Clang enables: %s",
|
||||
strings.Join(conflicts, "; "))
|
||||
}
|
||||
}
|
||||
|
||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||
// to recursive tests.
|
||||
|
||||
+3
-29
@@ -204,18 +204,7 @@ var avrBuiltins = []string{
|
||||
|
||||
// Builtins needed specifically for windows/386.
|
||||
var windowsI386Builtins = []string{
|
||||
"i386/chkstk.S", // __chkstk_ms
|
||||
"i386/chkstk2.S", // _alloca (__alloca)
|
||||
}
|
||||
|
||||
// Builtins needed specifically for windows/amd64.
|
||||
var windowsAMD64Builtins = []string{
|
||||
"x86_64/chkstk.S",
|
||||
}
|
||||
|
||||
// Builtins needed specifically for windows/arm64.
|
||||
var windowsARM64Builtins = []string{
|
||||
"aarch64/chkstk.S",
|
||||
"i386/chkstk.S", // also _alloca
|
||||
}
|
||||
|
||||
// libCompilerRT is a library with symbols required by programs compiled with
|
||||
@@ -244,28 +233,13 @@ var libCompilerRT = Library{
|
||||
builtins = append(builtins, aeabiBuiltins...)
|
||||
case "avr":
|
||||
builtins = append(builtins, avrBuiltins...)
|
||||
case "x86_64":
|
||||
builtins = append(builtins, genericBuiltins128...)
|
||||
if isWindowsTriple(target) {
|
||||
builtins = append(builtins, windowsAMD64Builtins...)
|
||||
}
|
||||
case "aarch64":
|
||||
builtins = append(builtins, genericBuiltins128...)
|
||||
if isWindowsTriple(target) {
|
||||
builtins = append(builtins, windowsARM64Builtins...)
|
||||
}
|
||||
case "riscv64":
|
||||
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
|
||||
builtins = append(builtins, genericBuiltins128...)
|
||||
case "i386":
|
||||
if isWindowsTriple(target) {
|
||||
if strings.Split(target, "-")[2] == "windows" {
|
||||
builtins = append(builtins, windowsI386Builtins...)
|
||||
}
|
||||
}
|
||||
return builtins, nil
|
||||
},
|
||||
}
|
||||
|
||||
func isWindowsTriple(target string) bool {
|
||||
parts := strings.Split(target, "-")
|
||||
return len(parts) > 2 && parts[2] == "windows"
|
||||
}
|
||||
|
||||
+1
-1
@@ -281,7 +281,7 @@ func parseDepFile(s string) ([]string, error) {
|
||||
s = strings.ReplaceAll(s, "\\\n", " ")
|
||||
|
||||
// Only use the first line, which is expected to begin with "deps:".
|
||||
line, _, _ := strings.Cut(s, "\n")
|
||||
line := strings.SplitN(s, "\n", 2)[0]
|
||||
if !strings.HasPrefix(line, "deps:") {
|
||||
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
||||
}
|
||||
|
||||
+17
-17
@@ -23,7 +23,7 @@
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Driver/DriverDiagnostic.h"
|
||||
#include "clang/Options/Options.h"
|
||||
#include "clang/Driver/Options.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
@@ -35,7 +35,6 @@
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCCodeEmitter.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCInstPrinter.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCObjectFileInfo.h"
|
||||
#include "llvm/MC/MCObjectWriter.h"
|
||||
@@ -68,7 +67,8 @@
|
||||
#include <optional>
|
||||
#include <system_error>
|
||||
using namespace clang;
|
||||
using namespace clang::options;
|
||||
using namespace clang::driver;
|
||||
using namespace clang::driver::options;
|
||||
using namespace llvm;
|
||||
using namespace llvm::opt;
|
||||
|
||||
@@ -109,7 +109,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
// Construct the invocation.
|
||||
|
||||
// Target Options
|
||||
Opts.Triple = llvm::Triple(llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)));
|
||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
||||
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
||||
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
|
||||
@@ -125,8 +125,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||
|
||||
// Use the default target triple if unspecified.
|
||||
if (Opts.Triple.getTriple().empty())
|
||||
Opts.Triple = llvm::Triple(llvm::sys::getDefaultTargetTriple());
|
||||
if (Opts.Triple.empty())
|
||||
Opts.Triple = llvm::sys::getDefaultTargetTriple();
|
||||
|
||||
// Language Options
|
||||
Opts.IncludePaths = Args.getAllArgValues(OPT_I);
|
||||
@@ -267,7 +267,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
std::string Error;
|
||||
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
||||
if (!TheTarget)
|
||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
|
||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
||||
@@ -327,7 +327,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
||||
assert(STI && "Unable to create subtarget info!");
|
||||
|
||||
MCContext Ctx(Opts.Triple, MAI.get(), MRI.get(), STI.get(), &SrcMgr,
|
||||
MCContext Ctx(Triple(Opts.Triple), MAI.get(), MRI.get(), STI.get(), &SrcMgr,
|
||||
&MCOptions);
|
||||
|
||||
bool PIC = false;
|
||||
@@ -390,8 +390,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
|
||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||
if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
|
||||
std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
|
||||
Opts.Triple, Opts.OutputAsmVariant, *MAI, *MCII, *MRI));
|
||||
MCInstPrinter *IP = TheTarget->createMCInstPrinter(
|
||||
llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI);
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE;
|
||||
if (Opts.ShowEncoding)
|
||||
@@ -400,7 +400,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
|
||||
auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
|
||||
Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), std::move(IP),
|
||||
Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), IP,
|
||||
std::move(CE), std::move(MAB)));
|
||||
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
||||
Str.reset(createNullStreamer(Ctx));
|
||||
@@ -422,7 +422,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
||||
: MAB->createObjectWriter(*Out);
|
||||
|
||||
Triple T = Opts.Triple;
|
||||
Triple T(Opts.Triple);
|
||||
Str.reset(TheTarget->createMCObjectStreamer(
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI));
|
||||
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||
@@ -453,7 +453,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
std::unique_ptr<MCTargetAsmParser> TAP(
|
||||
TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions));
|
||||
if (!TAP)
|
||||
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
|
||||
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
|
||||
// Set values for symbols, if any.
|
||||
for (auto &S : Opts.SymbolDefs) {
|
||||
@@ -506,12 +506,12 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
InitializeAllAsmParsers();
|
||||
|
||||
// Construct our diagnostic client.
|
||||
DiagnosticOptions DiagOpts;
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
TextDiagnosticPrinter *DiagClient
|
||||
= new TextDiagnosticPrinter(errs(), DiagOpts);
|
||||
= new TextDiagnosticPrinter(errs(), &*DiagOpts);
|
||||
DiagClient->setPrefix("clang -cc1as");
|
||||
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
DiagnosticsEngine Diags(DiagID, DiagOpts, DiagClient);
|
||||
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
|
||||
|
||||
// Set an error handler, so that any LLVM backend diagnostics go through our
|
||||
// error handler.
|
||||
@@ -528,7 +528,7 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
llvm::outs(), "clang -cc1as [options] file...",
|
||||
"Clang Integrated Assembler", /*ShowHidden=*/false,
|
||||
/*ShowAllAliases=*/false,
|
||||
llvm::opt::Visibility(clang::options::CC1AsOption));
|
||||
llvm::opt::Visibility(driver::options::CC1AsOption));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ struct AssemblerInvocation {
|
||||
/// @{
|
||||
|
||||
/// The name of the target triple to assemble for.
|
||||
llvm::Triple Triple;
|
||||
std::string Triple;
|
||||
|
||||
/// If given, the name of the target CPU to determine which instructions
|
||||
/// are legal.
|
||||
@@ -107,7 +107,7 @@ struct AssemblerInvocation {
|
||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||
|
||||
// Whether to emit compact-unwind for non-canonical entries.
|
||||
// Note: maybe overridden by other constraints.
|
||||
// Note: maybe overriden by other constraints.
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||
|
||||
@@ -142,7 +142,7 @@ struct AssemblerInvocation {
|
||||
|
||||
public:
|
||||
AssemblerInvocation() {
|
||||
Triple = llvm::Triple();
|
||||
Triple = "";
|
||||
NoInitialTextSection = 0;
|
||||
InputFile = "-";
|
||||
OutputPath = "-";
|
||||
|
||||
+4
-4
@@ -27,9 +27,9 @@ bool tinygo_clang_driver(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
|
||||
// The compiler invocation needs a DiagnosticsEngine so it can report problems
|
||||
clang::DiagnosticOptions DiagOpts;
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
|
||||
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), DiagOpts, &DiagnosticPrinter, false);
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = new clang::DiagnosticOptions();
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
|
||||
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), &*DiagOpts, &DiagnosticPrinter, false);
|
||||
|
||||
// Create the clang driver
|
||||
clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
||||
@@ -60,7 +60,7 @@ bool tinygo_clang_driver(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Create the actual diagnostics engine.
|
||||
Clang->createDiagnostics();
|
||||
Clang->createDiagnostics(*llvm::vfs::getRealFileSystem());
|
||||
if (!Clang->hasDiagnostics()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ import (
|
||||
var commands = map[string][]string{}
|
||||
|
||||
func init() {
|
||||
llvmMajor, _, _ := strings.Cut(llvm.Version, ".")
|
||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
||||
commands["clang"] = []string{"clang-" + llvmMajor}
|
||||
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
||||
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
}
|
||||
|
||||
// Version range supported by TinyGo.
|
||||
const minorMin = 25 // when updating the min version, also update .github/workflows/compat.yml
|
||||
const minorMax = 27
|
||||
const minorMin = 19
|
||||
const minorMax = 25
|
||||
|
||||
// Check that we support this Go toolchain version.
|
||||
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
|
||||
|
||||
@@ -15,7 +15,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
||||
return &compileJob{
|
||||
description: "compile Darwin libSystem.dylib",
|
||||
run: func(job *compileJob) (err error) {
|
||||
arch, _, _ := strings.Cut(config.Triple(), "-")
|
||||
arch := strings.Split(config.Triple(), "-")[0]
|
||||
job.result = filepath.Join(tmpdir, "libSystem.dylib")
|
||||
objpath := filepath.Join(tmpdir, "libSystem.o")
|
||||
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
|
||||
|
||||
+12
-147
@@ -65,6 +65,15 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
// Sort the segments by address. This is what esptool does too.
|
||||
sort.SliceStable(segments, func(i, j int) bool { return segments[i].addr < segments[j].addr })
|
||||
|
||||
// Calculate checksum over the segment data. This is used in the image
|
||||
// footer.
|
||||
checksum := uint8(0xef)
|
||||
for _, segment := range segments {
|
||||
for _, b := range segment.data {
|
||||
checksum ^= b
|
||||
}
|
||||
}
|
||||
|
||||
// Write first to an in-memory buffer, primarily so that we can easily
|
||||
// calculate a hash over the entire image.
|
||||
// An added benefit is that we don't need to check for errors all the time.
|
||||
@@ -79,86 +88,6 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
chip = format[:len(format)-len("-img")]
|
||||
}
|
||||
|
||||
// For ESP32 (original): separate RAM segments (loadable by ROM bootloader)
|
||||
// from flash-mapped segments (DROM/IROM, require MMU setup by startup code).
|
||||
// The ROM bootloader on ESP32 does NOT handle flash-mapped segments —
|
||||
// it tries to memcpy to the virtual address, which crashes.
|
||||
var flashSegments []*espImageSegment
|
||||
if chip == "esp32" {
|
||||
var ramSegments []*espImageSegment
|
||||
for _, seg := range segments {
|
||||
if (seg.addr >= 0x3F400000 && seg.addr < 0x3F800000) || // DROM
|
||||
(seg.addr >= 0x400D0000 && seg.addr < 0x40400000) { // IROM
|
||||
flashSegments = append(flashSegments, seg)
|
||||
} else {
|
||||
ramSegments = append(ramSegments, seg)
|
||||
}
|
||||
}
|
||||
segments = ramSegments
|
||||
}
|
||||
|
||||
// ESP32 flash XIP: compute where the DROM segment will be placed in flash
|
||||
// (page-aligned, right after the RAM segments) and patch the
|
||||
// _drom_flash_addr variable so the startup code can program the cache MMU.
|
||||
// This must happen before the checksum/hash are computed so the patched
|
||||
// value is covered by both.
|
||||
const esp32FlashBase = 0x1000 // esptool flashes the image at 0x1000
|
||||
// The ESP32 flash cache MMU supports configurable page sizes down to 256 B. 64 KiB is the reset/default size.
|
||||
// If the startup code ever changes the MMU page size, this constant must change too.
|
||||
const esp32PageSize = 0x10000 // 64KB MMU pages
|
||||
var esp32DromFlashAddr uint32
|
||||
if chip == "esp32" && len(flashSegments) > 0 {
|
||||
// Compute the size of the RAM portion of the image (everything the ROM
|
||||
// bootloader loads, up to and including the appended SHA256 hash).
|
||||
ramImageSize := 0
|
||||
if makeImage {
|
||||
ramImageSize += 4096
|
||||
}
|
||||
ramImageSize += 24 // image header (8) + trailer fields (16)
|
||||
for _, seg := range segments {
|
||||
ramImageSize += 8 + len(seg.data) // segment header + data (4-aligned)
|
||||
}
|
||||
ramImageSize += 16 - ramImageSize%16 // footer padding + checksum byte
|
||||
ramImageSize += 32 // appended SHA256 hash
|
||||
|
||||
// DROM flash address must be 64KB page-aligned.
|
||||
esp32DromFlashAddr = uint32(esp32FlashBase+ramImageSize+esp32PageSize-1) &^ (esp32PageSize - 1)
|
||||
|
||||
// Patch _drom_flash_addr in whichever RAM segment contains it.
|
||||
syms, _ := inf.Symbols()
|
||||
var dromSymAddr uint64
|
||||
for _, s := range syms {
|
||||
if s.Name == "_drom_flash_addr" {
|
||||
dromSymAddr = s.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
if dromSymAddr == 0 {
|
||||
return fmt.Errorf("ESP32: _drom_flash_addr symbol not found")
|
||||
}
|
||||
patched := false
|
||||
for _, seg := range segments {
|
||||
if dromSymAddr >= uint64(seg.addr) && dromSymAddr+4 <= uint64(seg.addr)+uint64(len(seg.data)) {
|
||||
off := int(dromSymAddr - uint64(seg.addr))
|
||||
binary.LittleEndian.PutUint32(seg.data[off:], esp32DromFlashAddr)
|
||||
patched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !patched {
|
||||
return fmt.Errorf("ESP32: _drom_flash_addr (0x%x) not in any RAM segment", dromSymAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate checksum over the segment data. This is used in the image
|
||||
// footer.
|
||||
checksum := uint8(0xef)
|
||||
for _, segment := range segments {
|
||||
for _, b := range segment.data {
|
||||
checksum ^= b
|
||||
}
|
||||
}
|
||||
|
||||
if makeImage {
|
||||
// The bootloader starts at 0x1000, or 4096.
|
||||
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||
@@ -171,24 +100,12 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
"esp32c6": 0x000d,
|
||||
"esp32s3": 0x0009,
|
||||
}[chip]
|
||||
|
||||
// SPI flash speed/size byte (byte 3 of header):
|
||||
// Upper nibble = flash size, lower nibble = flash frequency.
|
||||
// The espflasher auto-detects and patches the flash size (upper nibble),
|
||||
// but the frequency (lower nibble) must be correct per chip.
|
||||
spiSpeedSize := map[string]uint8{
|
||||
"esp32": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
"esp32c3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
"esp32c6": 0x10, // 80MHz=0x00, 2MB=0x10 (C6 uses different freq encoding)
|
||||
"esp32s3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
}[chip]
|
||||
|
||||
// Image header.
|
||||
switch chip {
|
||||
case "esp32", "esp32c3", "esp32s3", "esp32c6":
|
||||
case "esp32", "esp32c3", "esp32s3":
|
||||
// Header format:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||
// Note: not adding a SHA256 hash as the binary is modified by
|
||||
@@ -209,8 +126,8 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
}{
|
||||
magic: 0xE9,
|
||||
segment_count: byte(len(segments)),
|
||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||
spi_speed_size: spiSpeedSize,
|
||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||
spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB
|
||||
entry_addr: uint32(inf.Entry),
|
||||
wp_pin: 0xEE, // disable WP pin
|
||||
chip_id: chip_id,
|
||||
@@ -262,58 +179,6 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
outf.Write(hash[:])
|
||||
}
|
||||
|
||||
// For ESP32: append flash-mapped segments (DROM/IROM) at page-aligned flash
|
||||
// offsets after the RAM portion. The startup code maps them via the flash
|
||||
// cache MMU (DROM at esp32DromFlashAddr, patched into _drom_flash_addr).
|
||||
if len(flashSegments) > 0 {
|
||||
const flashBase = esp32FlashBase
|
||||
const pageSize = esp32PageSize
|
||||
dromFlashAddr := esp32DromFlashAddr
|
||||
|
||||
// Separate DROM and IROM segments.
|
||||
var dromSegs, iromSegs []*espImageSegment
|
||||
for _, seg := range flashSegments {
|
||||
if seg.addr >= 0x3F400000 && seg.addr < 0x3F800000 {
|
||||
dromSegs = append(dromSegs, seg)
|
||||
} else {
|
||||
iromSegs = append(iromSegs, seg)
|
||||
}
|
||||
}
|
||||
|
||||
// Write DROM segments at the computed page-aligned flash offset.
|
||||
dromSize := 0
|
||||
if len(dromSegs) > 0 {
|
||||
targetImageOffset := int(dromFlashAddr - flashBase)
|
||||
if outf.Len() > targetImageOffset {
|
||||
return fmt.Errorf("ESP32: RAM segments too large (%d bytes), overlap DROM at flash 0x%x", outf.Len(), dromFlashAddr)
|
||||
}
|
||||
outf.Write(make([]byte, targetImageOffset-outf.Len()))
|
||||
for _, seg := range dromSegs {
|
||||
outf.Write(seg.data)
|
||||
dromSize += len(seg.data)
|
||||
}
|
||||
}
|
||||
|
||||
// Write IROM segments immediately after DROM, at the next page boundary.
|
||||
// IROM flash addr = dromFlashAddr + ceil(dromSize/pageSize)*pageSize
|
||||
// (must match the computation in the startup assembly).
|
||||
if len(iromSegs) > 0 {
|
||||
dromPages := (dromSize + pageSize - 1) / pageSize
|
||||
if dromPages == 0 {
|
||||
dromPages = 1
|
||||
}
|
||||
iromFlashAddr := dromFlashAddr + uint32(dromPages)*pageSize
|
||||
targetImageOffset := int(iromFlashAddr - flashBase)
|
||||
if outf.Len() > targetImageOffset {
|
||||
return fmt.Errorf("ESP32: DROM too large, overlaps IROM at flash 0x%x", iromFlashAddr)
|
||||
}
|
||||
outf.Write(make([]byte, targetImageOffset-outf.Len()))
|
||||
for _, seg := range iromSegs {
|
||||
outf.Write(seg.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||
// image to be a certain size.
|
||||
if makeImage {
|
||||
|
||||
+2
-2
@@ -195,11 +195,11 @@ type intHeap struct {
|
||||
sort.IntSlice
|
||||
}
|
||||
|
||||
func (h *intHeap) Push(x any) {
|
||||
func (h *intHeap) Push(x interface{}) {
|
||||
h.IntSlice = append(h.IntSlice, x.(int))
|
||||
}
|
||||
|
||||
func (h *intHeap) Pop() any {
|
||||
func (h *intHeap) Pop() interface{} {
|
||||
x := h.IntSlice[len(h.IntSlice)-1]
|
||||
h.IntSlice = h.IntSlice[:len(h.IntSlice)-1]
|
||||
return x
|
||||
|
||||
+5
-18
@@ -133,7 +133,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="+compileopts.ClangTriple(target), "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
resourceDir := goenv.ClangResourceDir(false)
|
||||
if resourceDir != "" {
|
||||
args = append(args, "-resource-dir="+resourceDir)
|
||||
@@ -167,9 +167,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// double.
|
||||
args = append(args, "-mdouble=64")
|
||||
case "riscv32":
|
||||
args = append(args, "-march="+riscvMarch(config, "rv32imac"), "-fforce-enable-int128")
|
||||
args = append(args, "-march=rv32imac", "-fforce-enable-int128")
|
||||
case "riscv64":
|
||||
args = append(args, "-march="+riscvMarch(config, "rv64gc"))
|
||||
args = append(args, "-march=rv64gc")
|
||||
case "mips":
|
||||
args = append(args, "-fno-pic")
|
||||
}
|
||||
@@ -218,7 +218,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
return err
|
||||
}
|
||||
// Store this archive in the cache.
|
||||
return robustRename(f.Name(), archiveFilePath)
|
||||
return os.Rename(f.Name(), archiveFilePath)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
}
|
||||
for _, path := range paths {
|
||||
// Strip leading "../" parts off the path.
|
||||
path := path
|
||||
cleanpath := path
|
||||
for strings.HasPrefix(cleanpath, "../") {
|
||||
cleanpath = cleanpath[3:]
|
||||
@@ -297,17 +298,3 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
once.Do(unlock)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// riscvMarch returns the -march value for RISC-V library compilation.
|
||||
// It extracts the value from the target's cflags if present, otherwise
|
||||
// falls back to the provided default. This ensures libraries are compiled
|
||||
// with the correct ISA extensions for each target (e.g. rv32imc for
|
||||
// ESP32-C3 which lacks the atomic extension).
|
||||
func riscvMarch(config *compileopts.Config, defaultMarch string) string {
|
||||
for _, flag := range config.Target.CFlags {
|
||||
if strings.HasPrefix(flag, "-march=") {
|
||||
return flag[len("-march="):]
|
||||
}
|
||||
}
|
||||
return defaultMarch
|
||||
}
|
||||
|
||||
+2
-2
@@ -28,8 +28,8 @@ func buildMuslAllTypes(arch, muslDir, outputBitsDir string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.SplitSeq(string(data), "\n")
|
||||
for line := range lines {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "TYPEDEF ") {
|
||||
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
||||
value := matches[1]
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package builder
|
||||
|
||||
import "os"
|
||||
|
||||
func robustRename(oldpath, newpath string) error {
|
||||
return os.Rename(oldpath, newpath)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const robustRenameTimeout = 2 * time.Second
|
||||
|
||||
func robustRename(oldpath, newpath string) error {
|
||||
var bestErr error
|
||||
start := time.Now()
|
||||
nextSleep := time.Millisecond
|
||||
for {
|
||||
err := os.Rename(oldpath, newpath)
|
||||
if err == nil || !isEphemeralRenameError(err) {
|
||||
return err
|
||||
}
|
||||
if bestErr == nil {
|
||||
bestErr = err
|
||||
}
|
||||
if d := time.Since(start) + nextSleep; d >= robustRenameTimeout {
|
||||
return bestErr
|
||||
}
|
||||
time.Sleep(nextSleep)
|
||||
nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
|
||||
}
|
||||
}
|
||||
|
||||
func isEphemeralRenameError(err error) bool {
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) {
|
||||
switch errno {
|
||||
case syscall.Errno(2), // ERROR_FILE_NOT_FOUND
|
||||
syscall.Errno(5), // ERROR_ACCESS_DENIED
|
||||
syscall.Errno(32): // ERROR_SHARING_VIOLATION
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
+3
-13
@@ -501,9 +501,8 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
Align: section.Addralign,
|
||||
Type: memoryStack,
|
||||
})
|
||||
} else if section.Flags&elf.SHF_WRITE != 0 {
|
||||
// Regular .bss section. Zero-initialized RAM is always
|
||||
// writable, so require SHF_WRITE here.
|
||||
} else {
|
||||
// Regular .bss section.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
@@ -511,11 +510,6 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
Type: memoryBSS,
|
||||
})
|
||||
}
|
||||
// Other (non-writable) SHT_NOBITS sections are address-space
|
||||
// placeholders that occupy no RAM, such as the ESP linker
|
||||
// script's .irom_dummy / .rodata_dummy sections which reserve
|
||||
// the flash-mapped XIP virtual address ranges. They must not be
|
||||
// counted as bss/RAM usage.
|
||||
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_EXECINSTR != 0 {
|
||||
// .text
|
||||
sections = append(sections, memorySection{
|
||||
@@ -960,11 +954,7 @@ func findPackagePath(path string, packagePathMap map[string]string) (packagePath
|
||||
libPath := strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")+string(os.PathSeparator))
|
||||
parts := strings.SplitN(libPath, string(os.PathSeparator), 2)
|
||||
packagePath = "C " + parts[0]
|
||||
if len(parts) > 1 {
|
||||
filename = parts[1]
|
||||
} else {
|
||||
filename = parts[0]
|
||||
}
|
||||
filename = parts[1]
|
||||
} else if prefix := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project", "compiler-rt"); strings.HasPrefix(path, prefix) {
|
||||
packagePath = "C compiler-rt"
|
||||
filename = strings.TrimPrefix(path, prefix+string(os.PathSeparator))
|
||||
|
||||
@@ -42,15 +42,16 @@ func TestBinarySize(t *testing.T) {
|
||||
// This is a small number of very diverse targets that we want to test.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 4313, 323, 0, 2260},
|
||||
{"microbit", "examples/serial", 2838, 382, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 8027, 1665, 132, 7488},
|
||||
{"hifive1b", "examples/echo", 3668, 280, 0, 2244},
|
||||
{"microbit", "examples/serial", 2694, 342, 8, 2248},
|
||||
{"wioterminal", "examples/pininterrupt", 6837, 1491, 120, 6888},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
// output varies by binaryen version.
|
||||
}
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.target+"/"+tc.path, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -84,6 +85,7 @@ func TestSizeFull(t *testing.T) {
|
||||
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
|
||||
|
||||
for _, target := range tests {
|
||||
target := target
|
||||
t.Run(target, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -97,7 +99,7 @@ func TestSizeFull(t *testing.T) {
|
||||
t.Fatal("could not read program size:", err)
|
||||
}
|
||||
for _, pkg := range sizes.sortedPackageNames() {
|
||||
if pkg == "(padding)" || pkg == "(unknown)" || pkg == "Go types" {
|
||||
if pkg == "(padding)" || pkg == "(unknown)" {
|
||||
// TODO: correctly attribute all unknown binary size.
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func RunTool(tool string, args ...string) error {
|
||||
var cflag *C.char
|
||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||
defer C.free(buf)
|
||||
cflags := unsafe.Slice((**C.char)(buf), len(args))
|
||||
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):len(args)]
|
||||
for i, flag := range args {
|
||||
cflag := C.CString(flag)
|
||||
cflags[i] = cflag
|
||||
|
||||
+3
-9
@@ -116,24 +116,18 @@ func parseLLDErrors(text string) error {
|
||||
// This can happen in some cases like with CGo and //go:linkname tricker.
|
||||
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
||||
symbolName := matches[2]
|
||||
for line := range strings.SplitSeq(message, "\n") {
|
||||
for _, line := range strings.Split(message, "\n") {
|
||||
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
||||
if matches != nil {
|
||||
parsedError = true
|
||||
line, _ := strconv.Atoi(matches[3])
|
||||
msg := "linker could not find symbol " + symbolName
|
||||
switch symbolName {
|
||||
case "runtime.alloc":
|
||||
msg = "object allocated on the heap with -gc=none"
|
||||
case "runtime.alloc_noheap":
|
||||
msg = "object allocated on the heap in //go:noheap function"
|
||||
}
|
||||
// TODO: detect common mistakes like -gc=none?
|
||||
linkErrors = append(linkErrors, scanner.Error{
|
||||
Pos: token.Position{
|
||||
Filename: matches[2],
|
||||
Line: line,
|
||||
},
|
||||
Msg: msg,
|
||||
Msg: "linker could not find symbol " + symbolName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byt
|
||||
}
|
||||
bl.SetNumBlocks(len(blocks))
|
||||
|
||||
for i := range blocks {
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
bl.SetBlockNo(i)
|
||||
bl.SetData(blocks[i])
|
||||
|
||||
|
||||
+30
-10
@@ -26,6 +26,10 @@ import (
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Function that's only defined in Go 1.22.
|
||||
var setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
}
|
||||
|
||||
// cgoPackage holds all CGo-related information of a package.
|
||||
type cgoPackage struct {
|
||||
generated *ast.File
|
||||
@@ -40,7 +44,7 @@ type cgoPackage struct {
|
||||
tokenFiles map[string]*token.File
|
||||
definedGlobally map[string]ast.Node
|
||||
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
||||
anonDecls map[any]string
|
||||
anonDecls map[interface{}]string
|
||||
cflags []string // CFlags from #cgo lines
|
||||
ldflags []string // LDFlags from #cgo lines
|
||||
visitedFiles map[string][]byte
|
||||
@@ -259,7 +263,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
tokenFiles: map[string]*token.File{},
|
||||
definedGlobally: map[string]ast.Node{},
|
||||
noescapingFuncs: map[string]*noescapingFunc{},
|
||||
anonDecls: map[any]string{},
|
||||
anonDecls: map[interface{}]string{},
|
||||
visitedFiles: map[string][]byte{},
|
||||
}
|
||||
|
||||
@@ -302,7 +306,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// Find `import "C"` C fragments in the file.
|
||||
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
|
||||
for i, f := range files {
|
||||
var cgoHeader strings.Builder
|
||||
var cgoHeader string
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
decl := f.Decls[i]
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
@@ -337,8 +341,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// Iterate through all parts of the CGo header. Note that every //
|
||||
// line is a new comment.
|
||||
position := fset.Position(genDecl.Doc.Pos())
|
||||
var fragment strings.Builder
|
||||
fragment.WriteString(fmt.Sprintf("# %d %#v\n", position.Line, position.Filename))
|
||||
fragment := fmt.Sprintf("# %d %#v\n", position.Line, position.Filename)
|
||||
for _, comment := range genDecl.Doc.List {
|
||||
// Find all #cgo lines, extract and use their contents, and
|
||||
// replace the lines with spaces (to preserve locations).
|
||||
@@ -355,13 +358,12 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
} else { // comment
|
||||
c = " " + c[2:len(c)-2]
|
||||
}
|
||||
fragment.WriteString(c)
|
||||
fragment.WriteByte('\n')
|
||||
fragment += c + "\n"
|
||||
}
|
||||
cgoHeader.WriteString(fragment.String())
|
||||
cgoHeader += fragment
|
||||
}
|
||||
|
||||
p.cgoHeaders[i] = cgoHeader.String()
|
||||
p.cgoHeaders[i] = cgoHeader
|
||||
}
|
||||
|
||||
// Define CFlags that will be used while parsing the package.
|
||||
@@ -652,6 +654,7 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "union",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: pos,
|
||||
@@ -705,6 +708,7 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -760,6 +764,7 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -806,6 +811,11 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
@@ -813,6 +823,7 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -870,6 +881,7 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -952,6 +964,11 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
@@ -959,6 +976,7 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -979,6 +997,7 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "value",
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
Type: bitfield.field.Type,
|
||||
@@ -996,6 +1015,7 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -1219,7 +1239,7 @@ func getPos(node ast.Node) token.Pos {
|
||||
// getUnnamedDeclName creates a name (with the given prefix) for the given C
|
||||
// declaration. This is used for structs, unions, and enums that are often
|
||||
// defined without a name and used in a typedef.
|
||||
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf any) string {
|
||||
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
|
||||
if name, ok := p.anonDecls[itf]; ok {
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build go1.22
|
||||
|
||||
package cgo
|
||||
|
||||
// Code specifically for Go 1.22.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
f.FileStart = start
|
||||
f.FileEnd = end
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ func TestCGo(t *testing.T) {
|
||||
"flags",
|
||||
"const",
|
||||
} {
|
||||
name := name // avoid a race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Read the AST in memory.
|
||||
path := filepath.Join("testdata", name+".go")
|
||||
|
||||
+70
-18
@@ -130,7 +130,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
// convert Go slice of strings to C array of strings.
|
||||
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
||||
defer C.free(cmdargsC)
|
||||
cmdargs := unsafe.Slice((**C.char)(cmdargsC), len(cflags))
|
||||
cmdargs := (*[1 << 16]*C.char)(cmdargsC)
|
||||
for i, cflag := range cflags {
|
||||
s := C.CString(cflag)
|
||||
cmdargs[i] = s
|
||||
@@ -160,7 +160,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
pos := f.getClangLocationPosition(location, unit)
|
||||
f.addError(pos, severity+": "+spelling)
|
||||
}
|
||||
for i := range numDiagnostics {
|
||||
for i := 0; i < numDiagnostics; i++ {
|
||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||
addDiagnostic(diagnostic)
|
||||
|
||||
@@ -190,7 +190,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
// Sanity check. This should (hopefully) never trigger.
|
||||
panic("libclang: file contents was not loaded")
|
||||
}
|
||||
data := unsafe.Slice((*byte)(unsafe.Pointer(rawData)), size)
|
||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||
|
||||
// Hash the contents if it isn't hashed yet.
|
||||
if _, ok := f.visitedFiles[path]; !ok {
|
||||
@@ -217,6 +217,10 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
exportName := name
|
||||
localName := name
|
||||
var stringSignature string
|
||||
@@ -254,6 +258,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + localName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: pos,
|
||||
@@ -278,7 +283,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Text: strings.Join(doc, "\n"),
|
||||
})
|
||||
}
|
||||
for i := range numArgs {
|
||||
for i := 0; i < numArgs; i++ {
|
||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||
@@ -290,6 +295,11 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: argName,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: argName,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: f.makeDecayingASTType(argType, pos),
|
||||
@@ -305,6 +315,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
}
|
||||
}
|
||||
obj.Decl = decl
|
||||
return decl, stringSignature
|
||||
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||
typ := f.makeASTRecordType(c, pos)
|
||||
@@ -314,25 +325,39 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
// Convert to a single-field struct type.
|
||||
typeExpr = f.makeUnionField(typ)
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typ.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, typ
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typeName := "_Cgo_" + name
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Assign: pos,
|
||||
Type: f.makeASTType(underlyingType, pos),
|
||||
Type: f.makeASTType(underlyingType, pos),
|
||||
}
|
||||
if underlyingType.kind != C.CXType_Enum {
|
||||
typeSpec.Assign = pos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_VarDecl:
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
@@ -351,13 +376,19 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
},
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_MacroDefinition:
|
||||
@@ -374,16 +405,26 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_EnumDecl:
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||
// instead of types such as C.int, which are used here.
|
||||
@@ -391,10 +432,12 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Assign: pos,
|
||||
Type: f.makeASTType(underlying, pos),
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
@@ -409,13 +452,19 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "_Cgo_" + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "_Cgo_" + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
default:
|
||||
@@ -532,7 +581,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
|
||||
// Get the precise location in the source code. Used for uniquely identifying
|
||||
// source locations.
|
||||
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) any {
|
||||
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
|
||||
@@ -575,7 +624,7 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
||||
// now by reading the file from libclang.
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
source := unsafe.Slice((*byte)(unsafe.Pointer(sourcePtr)), size)
|
||||
source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size]
|
||||
lines := []int{0}
|
||||
for i := 0; i < len(source)-1; i++ {
|
||||
if source[i] == '\n' {
|
||||
@@ -590,8 +639,7 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
||||
Package: f.Pos(0),
|
||||
Name: ast.NewIdent(p.packageName),
|
||||
}
|
||||
astFile.FileStart = f.Pos(0)
|
||||
astFile.FileEnd = f.Pos(int(size))
|
||||
setASTFileFields(astFile, f.Pos(0), f.Pos(int(size)))
|
||||
p.cgoFiles = append(p.cgoFiles, astFile)
|
||||
}
|
||||
positionFile := p.tokenFiles[filename]
|
||||
@@ -804,14 +852,6 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
typeName = "<unknown>"
|
||||
}
|
||||
case C.CXType_Unexposed:
|
||||
// LLVM 22+ may report certain builtin type aliases (e.g. __size_t)
|
||||
// as Unexposed. Resolve via the canonical type.
|
||||
canonical := C.clang_getCanonicalType(typ)
|
||||
if canonical.kind != C.CXType_Unexposed && canonical.kind != C.CXType_Invalid {
|
||||
return f.makeASTType(canonical, pos)
|
||||
}
|
||||
// If still unexposed, fall through to the error below.
|
||||
case C.CXType_Record:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
@@ -916,16 +956,22 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
|
||||
}
|
||||
|
||||
// Construct an *ast.TypeSpec for this type.
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: name,
|
||||
}
|
||||
spec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: goName,
|
||||
},
|
||||
}
|
||||
obj.Decl = spec
|
||||
return spec
|
||||
}
|
||||
|
||||
@@ -1058,6 +1104,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
pos: prevField.Names[0].NamePos,
|
||||
})
|
||||
prevField.Names[0].Name = bitfieldName
|
||||
prevField.Names[0].Obj.Name = bitfieldName
|
||||
}
|
||||
prevBitfield := &(*bitfieldList)[len(*bitfieldList)-1]
|
||||
prevBitfield.endBit = bitfieldOffset
|
||||
@@ -1074,6 +1121,11 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: name,
|
||||
Decl: field,
|
||||
},
|
||||
},
|
||||
}
|
||||
fieldList.List = append(fieldList.List, field)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include -I/usr/lib64/llvm19/include
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm21 && !llvm22
|
||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include -I/usr/lib64/llvm20/include
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//go:build !byollvm && llvm21
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-21 -I/usr/include/llvm-c-21 -I/usr/lib/llvm-21/include -I/usr/lib64/llvm21/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@21/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@21/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm21/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-21/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@21/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@21/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm21/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,15 +0,0 @@
|
||||
//go:build !byollvm && llvm22
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-22 -I/usr/include/llvm-c-22 -I/usr/lib/llvm-22/include -I/usr/lib64/llvm22/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@22/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@22/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm22/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-22/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@22/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@22/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm22/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -78,7 +78,6 @@ var validCompilerFlags = []*regexp.Regexp{
|
||||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||
re(`-f(no-)?plt`),
|
||||
re(`-f(no-)?rtti`),
|
||||
re(`-f(no-)?short-enums`),
|
||||
re(`-f(no-)?split-stack`),
|
||||
re(`-f(no-)?stack-(.+)`),
|
||||
re(`-f(no-)?strict-aliasing`),
|
||||
|
||||
+4
-4
@@ -12,17 +12,17 @@ import "C"
|
||||
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
||||
// pointer but only a C pointer.
|
||||
type refMap struct {
|
||||
refs map[unsafe.Pointer]any
|
||||
refs map[unsafe.Pointer]interface{}
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Put stores a value in the map. It can later be retrieved using Get. It must
|
||||
// be removed using Remove to avoid memory leaks.
|
||||
func (m *refMap) Put(v any) unsafe.Pointer {
|
||||
func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
if m.refs == nil {
|
||||
m.refs = make(map[unsafe.Pointer]any, 1)
|
||||
m.refs = make(map[unsafe.Pointer]interface{}, 1)
|
||||
}
|
||||
ref := C.malloc(1)
|
||||
m.refs[ref] = v
|
||||
@@ -31,7 +31,7 @@ func (m *refMap) Put(v any) unsafe.Pointer {
|
||||
|
||||
// Get returns a stored value previously inserted with Put. Use the same
|
||||
// reference as you got from Put.
|
||||
func (m *refMap) Get(ref unsafe.Pointer) any {
|
||||
func (m *refMap) Get(ref unsafe.Pointer) interface{} {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
return m.refs[ref]
|
||||
|
||||
+16
-29
@@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -61,15 +60,13 @@ func (c *Config) BuildMode() string {
|
||||
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
|
||||
// will be returned.
|
||||
func (c *Config) Features() string {
|
||||
var features string
|
||||
if c.Target.Features == "" {
|
||||
features = c.Options.LLVMFeatures
|
||||
} else if c.Options.LLVMFeatures == "" {
|
||||
features = c.Target.Features
|
||||
} else {
|
||||
features = c.Target.Features + "," + c.Options.LLVMFeatures
|
||||
return c.Options.LLVMFeatures
|
||||
}
|
||||
return patchFeatures(features)
|
||||
if c.Options.LLVMFeatures == "" {
|
||||
return c.Target.Features
|
||||
}
|
||||
return c.Target.Features + "," + c.Options.LLVMFeatures
|
||||
}
|
||||
|
||||
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
|
||||
@@ -143,7 +140,13 @@ func (c *Config) GC() string {
|
||||
func (c *Config) NeedsStackObjects() bool {
|
||||
switch c.GC() {
|
||||
case "conservative", "custom", "precise", "boehm":
|
||||
return slices.Contains(c.BuildTags(), "tinygo.wasm")
|
||||
for _, tag := range c.BuildTags() {
|
||||
if tag == "tinygo.wasm" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -242,7 +245,7 @@ func (c *Config) RP2040BootPatch() bool {
|
||||
// Return a canonicalized architecture name, so we don't have to deal with arm*
|
||||
// vs thumb* vs arm64.
|
||||
func CanonicalArchName(triple string) string {
|
||||
arch, _, _ := strings.Cut(triple, "-")
|
||||
arch := strings.Split(triple, "-")[0]
|
||||
if arch == "arm64" {
|
||||
return "aarch64"
|
||||
}
|
||||
@@ -349,7 +352,7 @@ func (c *Config) CFlags(libclang bool) []string {
|
||||
// Use the same optimization level as TinyGo.
|
||||
cflags = append(cflags, "-O"+c.Options.Opt)
|
||||
// Set the LLVM target triple.
|
||||
cflags = append(cflags, "--target="+ClangTriple(c.Triple()))
|
||||
cflags = append(cflags, "--target="+c.Triple())
|
||||
// Set the -mcpu (or similar) flag.
|
||||
if c.Target.CPU != "" {
|
||||
if c.GOARCH() == "amd64" || c.GOARCH() == "386" {
|
||||
@@ -463,22 +466,6 @@ func (c *Config) LDFlags() []string {
|
||||
return ldflags
|
||||
}
|
||||
|
||||
// LinkerFlavor returns how the configured linker should be driven.
|
||||
// Usually this is derived from GOOS, but targets may override it explicitly.
|
||||
func (c *Config) LinkerFlavor() string {
|
||||
if c.Target.LinkerFlavor != "" {
|
||||
return c.Target.LinkerFlavor
|
||||
}
|
||||
switch c.GOOS() {
|
||||
case "windows":
|
||||
return "coff"
|
||||
case "darwin":
|
||||
return "darwin"
|
||||
default:
|
||||
return "gnu"
|
||||
}
|
||||
}
|
||||
|
||||
// ExtraFiles returns the list of extra files to be built and linked with the
|
||||
// executable. This can include extra C and assembly files.
|
||||
func (c *Config) ExtraFiles() []string {
|
||||
@@ -549,10 +536,10 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
||||
case "":
|
||||
// No configuration supplied.
|
||||
return c.Target.FlashMethod, c.Target.OpenOCDInterface
|
||||
case "openocd", "msd", "command", "adb":
|
||||
case "openocd", "msd", "command":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||
case "bmp", "probe-rs":
|
||||
case "bmp":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, ""
|
||||
default:
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
//go:build !llvm22 && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
|
||||
|
||||
package compileopts
|
||||
|
||||
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||
// For LLVM 20/21, features in the target JSON files are already correct.
|
||||
func patchFeatures(features string) string {
|
||||
return features
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//go:build llvm22
|
||||
|
||||
package compileopts
|
||||
|
||||
import "strings"
|
||||
|
||||
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||
// LLVM 22 renamed several Xtensa target features.
|
||||
func patchFeatures(features string) string {
|
||||
// Xtensa feature renames in LLVM 22:
|
||||
// atomctl → (removed, no direct replacement)
|
||||
// memctl → (removed, no direct replacement)
|
||||
// esp32s3 → esp32s3ops
|
||||
// timerint → timers3 (for esp32/esp32s3) or timers1 (for esp8266)
|
||||
// Since we can't distinguish which timer variant at this level,
|
||||
// just remove the obsolete features. The CPU definition already
|
||||
// implies the correct features in LLVM 22.
|
||||
replacer := strings.NewReplacer(
|
||||
"+atomctl,", "",
|
||||
"+memctl,", "",
|
||||
"+esp32s3,", "+esp32s3ops,",
|
||||
"+timerint,", "",
|
||||
",+timerint", "",
|
||||
)
|
||||
return replacer.Replace(features)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
//go:build llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19
|
||||
|
||||
package compileopts
|
||||
|
||||
import "strings"
|
||||
|
||||
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||
// LLVM 19 and earlier do not have +bulk-memory-opt or
|
||||
// +call-indirect-overlong for WebAssembly (added in LLVM 20).
|
||||
func patchFeatures(features string) string {
|
||||
features = strings.ReplaceAll(features, ",+bulk-memory-opt", "")
|
||||
features = strings.ReplaceAll(features, "+bulk-memory-opt,", "")
|
||||
features = strings.ReplaceAll(features, ",+call-indirect-overlong", "")
|
||||
features = strings.ReplaceAll(features, "+call-indirect-overlong,", "")
|
||||
return features
|
||||
}
|
||||
+55
-49
@@ -3,7 +3,6 @@ package compileopts
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -22,53 +21,51 @@ var (
|
||||
// usually passed from the command line, but can also be passed in environment
|
||||
// variables for example.
|
||||
type Options struct {
|
||||
GOOS string // environment variable
|
||||
GOARCH string // environment variable
|
||||
GOARM string // environment variable (only used with GOARCH=arm)
|
||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
||||
Directory string // working dir, leave it unset to use the current working dir
|
||||
Target string
|
||||
BuildMode string // -buildmode flag
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
||||
Serial string
|
||||
Work bool // -work flag to print temporary build directory
|
||||
InterpTimeout time.Duration
|
||||
InterpMaxLoopIterations int
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
SkipDWARF bool
|
||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||
Debug bool
|
||||
Nobounds bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintAllocsCover bool // emit allocs in go coverage tool format
|
||||
PrintStacks bool
|
||||
Tags []string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
ExtLDFlags []string
|
||||
GoCompatibility bool // enable to check for Go version compatibility
|
||||
GOOS string // environment variable
|
||||
GOARCH string // environment variable
|
||||
GOARM string // environment variable (only used with GOARCH=arm)
|
||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
||||
Directory string // working dir, leave it unset to use the current working dir
|
||||
Target string
|
||||
BuildMode string // -buildmode flag
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
||||
Serial string
|
||||
Work bool // -work flag to print temporary build directory
|
||||
InterpTimeout time.Duration
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
SkipDWARF bool
|
||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||
Debug bool
|
||||
Nobounds bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags []string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
ExtLDFlags []string
|
||||
GoCompatibility bool // enable to check for Go version compatibility
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
func (o *Options) Verify() error {
|
||||
if o.BuildMode != "" {
|
||||
valid := slices.Contains(validBuildModeOptions, o.BuildMode)
|
||||
valid := isInArray(validBuildModeOptions, o.BuildMode)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
|
||||
o.BuildMode,
|
||||
@@ -76,7 +73,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
}
|
||||
if o.GC != "" {
|
||||
valid := slices.Contains(validGCOptions, o.GC)
|
||||
valid := isInArray(validGCOptions, o.GC)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
|
||||
o.GC,
|
||||
@@ -85,7 +82,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.Scheduler != "" {
|
||||
valid := slices.Contains(validSchedulerOptions, o.Scheduler)
|
||||
valid := isInArray(validSchedulerOptions, o.Scheduler)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
|
||||
o.Scheduler,
|
||||
@@ -94,7 +91,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.Serial != "" {
|
||||
valid := slices.Contains(validSerialOptions, o.Serial)
|
||||
valid := isInArray(validSerialOptions, o.Serial)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
||||
o.Serial,
|
||||
@@ -103,7 +100,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.PrintSizes != "" {
|
||||
valid := slices.Contains(validPrintSizeOptions, o.PrintSizes)
|
||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
|
||||
o.PrintSizes,
|
||||
@@ -112,7 +109,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.PanicStrategy != "" {
|
||||
valid := slices.Contains(validPanicStrategyOptions, o.PanicStrategy)
|
||||
valid := isInArray(validPanicStrategyOptions, o.PanicStrategy)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
|
||||
o.PanicStrategy,
|
||||
@@ -121,10 +118,19 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.Opt != "" {
|
||||
if !slices.Contains(validOptOptions, o.Opt) {
|
||||
if !isInArray(validOptOptions, o.Opt) {
|
||||
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isInArray(arr []string, item string) bool {
|
||||
for _, i := range arr {
|
||||
if i == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+1
-26
@@ -24,7 +24,6 @@ import (
|
||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||
type TargetSpec struct {
|
||||
Inherits []string `json:"inherits,omitempty"`
|
||||
InheritableOnly bool `json:"inheritable-only"` // this target is only meant to be inherited from, not used directly
|
||||
Triple string `json:"llvm-target,omitempty"`
|
||||
CPU string `json:"cpu,omitempty"`
|
||||
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
|
||||
@@ -38,8 +37,7 @@ type TargetSpec struct {
|
||||
Scheduler string `json:"scheduler,omitempty"`
|
||||
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
||||
Linker string `json:"linker,omitempty"`
|
||||
LinkerFlavor string `json:"linker-flavor,omitempty"` // how to drive the configured linker (for example: gnu, coff, darwin)
|
||||
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc,omitempty"`
|
||||
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
|
||||
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
|
||||
@@ -65,10 +63,6 @@ type TargetSpec struct {
|
||||
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device,omitempty"`
|
||||
ADBPreCommands []string `json:"adb-pre-commands,omitempty"`
|
||||
ADBPushRemote string `json:"adb-push-remote,omitempty"`
|
||||
ADBPostCommands []string `json:"adb-post-commands,omitempty"`
|
||||
ProbeRSChip string `json:"probe-rs-chip,omitempty"`
|
||||
CodeModel string `json:"code-model,omitempty"`
|
||||
RelocationModel string `json:"relocation-model,omitempty"`
|
||||
WITPackage string `json:"wit-package,omitempty"`
|
||||
@@ -154,11 +148,6 @@ func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
||||
|
||||
// resolveInherits loads inherited targets, recursively.
|
||||
func (spec *TargetSpec) resolveInherits() error {
|
||||
// Save InheritableOnly before resolving, since it must not propagate
|
||||
// from parent to child (a board target should not become inheritable-only
|
||||
// just because its parent processor target is).
|
||||
inheritableOnly := spec.InheritableOnly
|
||||
|
||||
// First create a new spec with all the inherited properties.
|
||||
newSpec := &TargetSpec{}
|
||||
for _, name := range spec.Inherits {
|
||||
@@ -184,9 +173,6 @@ func (spec *TargetSpec) resolveInherits() error {
|
||||
}
|
||||
*spec = *newSpec
|
||||
|
||||
// Restore InheritableOnly from the original spec, not from parents.
|
||||
spec.InheritableOnly = inheritableOnly
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -253,17 +239,10 @@ func GetTargetSpecs() (map[string]*TargetSpec, error) {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, entry.Name())
|
||||
|
||||
spec, err := LoadTarget(&Options{Target: path})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list target: %w", err)
|
||||
}
|
||||
|
||||
if spec.InheritableOnly {
|
||||
// Skip targets that are only meant to be inherited from, not used directly.
|
||||
continue
|
||||
}
|
||||
|
||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||
// This doesn't look like a regular target file, but rather like
|
||||
// a parent target (such as targets/cortex-m.json).
|
||||
@@ -475,12 +454,10 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
"-m", "i386pep",
|
||||
"--image-base", "0x400000",
|
||||
)
|
||||
spec.RTLib = "compiler-rt"
|
||||
case "arm64":
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "arm64pe",
|
||||
)
|
||||
spec.RTLib = "compiler-rt"
|
||||
}
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-Bdynamic",
|
||||
@@ -488,8 +465,6 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/runtime_windows.c")
|
||||
case "wasm", "wasip1", "wasip2":
|
||||
return nil, fmt.Errorf("GOOS=%s but GOARCH is unset. Please set GOARCH to wasm", options.GOOS)
|
||||
default:
|
||||
|
||||
@@ -23,37 +23,6 @@ func TestLoadTarget(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTargetSpecs_InheritableOnlyTargetsExcluded(t *testing.T) {
|
||||
specs, err := GetTargetSpecs()
|
||||
if err != nil {
|
||||
t.Fatal("GetTargetSpecs failed:", err)
|
||||
}
|
||||
|
||||
// Inheritable-only processor-level targets should not appear in the listing.
|
||||
inheritableOnlyTargets := []string{"esp32", "esp32c3", "esp32s3", "esp8266", "rp2040", "rp2350", "rp2350b"}
|
||||
for _, name := range inheritableOnlyTargets {
|
||||
if _, ok := specs[name]; ok {
|
||||
t.Errorf("inheritable-only target %q should not appear in GetTargetSpecs", name)
|
||||
}
|
||||
}
|
||||
|
||||
// Board targets that inherit from inheritable-only targets should still appear.
|
||||
boardTargets := []string{"esp32-coreboard-v2", "pico"}
|
||||
for _, name := range boardTargets {
|
||||
if _, ok := specs[name]; !ok {
|
||||
t.Errorf("board target %q should appear in GetTargetSpecs", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadTarget_InheritableOnlyTargetStillLoadable(t *testing.T) {
|
||||
// Inheritable-only targets should still be loadable directly (for building).
|
||||
_, err := LoadTarget(&Options{Target: "esp32"})
|
||||
if err != nil {
|
||||
t.Errorf("LoadTarget should still load inheritable-only target esp32: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverrideProperties(t *testing.T) {
|
||||
baseAutoStackSize := true
|
||||
base := &TargetSpec{
|
||||
@@ -112,52 +81,3 @@ func TestOverrideProperties(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConfigLinkerFlavor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
target *TargetSpec
|
||||
goos string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "default gnu",
|
||||
target: &TargetSpec{},
|
||||
goos: "linux",
|
||||
want: "gnu",
|
||||
},
|
||||
{
|
||||
name: "default coff",
|
||||
target: &TargetSpec{},
|
||||
goos: "windows",
|
||||
want: "coff",
|
||||
},
|
||||
{
|
||||
name: "default darwin",
|
||||
target: &TargetSpec{},
|
||||
goos: "darwin",
|
||||
want: "darwin",
|
||||
},
|
||||
{
|
||||
name: "target override",
|
||||
target: &TargetSpec{
|
||||
LinkerFlavor: "coff",
|
||||
},
|
||||
goos: "linux",
|
||||
want: "coff",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.target.GOOS = tc.goos
|
||||
config := &Config{
|
||||
Options: &Options{},
|
||||
Target: tc.target,
|
||||
}
|
||||
if got := config.LinkerFlavor(); got != tc.want {
|
||||
t.Fatalf("LinkerFlavor() = %q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
//go:build llvm22
|
||||
|
||||
package compileopts
|
||||
|
||||
import "strings"
|
||||
|
||||
// ClangTriple returns the target triple to pass to Clang's --target flag.
|
||||
// LLVM 22 deprecated the "wasm32-unknown-wasi" triple in favor of
|
||||
// "wasm32-unknown-wasip1", so we substitute it here to avoid warnings.
|
||||
func ClangTriple(triple string) string {
|
||||
return strings.Replace(triple, "wasm32-unknown-wasi", "wasm32-unknown-wasip1", 1)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
//go:build !llvm22
|
||||
|
||||
package compileopts
|
||||
|
||||
// ClangTriple returns the target triple to pass to Clang's --target flag.
|
||||
// For pre-LLVM 22, the triple is used as-is.
|
||||
func ClangTriple(triple string) string {
|
||||
return triple
|
||||
}
|
||||
+8
-21
@@ -241,35 +241,22 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
||||
}
|
||||
}
|
||||
|
||||
faultBlock := b.getRuntimeAssertBlock(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")
|
||||
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
|
||||
|
||||
// Now branch to the out-of-bounds or the regular block.
|
||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||
|
||||
// Ok: assert didn't trigger so continue normally.
|
||||
b.SetInsertPointAtEnd(nextBlock)
|
||||
}
|
||||
|
||||
func (b *builder) getRuntimeAssertBlock(blockPrefix, assertFunc string) llvm.BasicBlock {
|
||||
if b.runtimeAssertBlocks == nil {
|
||||
b.runtimeAssertBlocks = make(map[string]llvm.BasicBlock)
|
||||
}
|
||||
if block := b.runtimeAssertBlocks[assertFunc]; !block.IsNil() {
|
||||
return block
|
||||
}
|
||||
savedBlock := b.GetInsertBlock()
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
b.runtimeAssertBlocks[assertFunc] = block
|
||||
b.SetInsertPointAtEnd(block)
|
||||
if b.hasDeferFrame() {
|
||||
b.createFaultCheckpoint()
|
||||
}
|
||||
// Fail: the assert triggered so panic.
|
||||
b.SetInsertPointAtEnd(faultBlock)
|
||||
b.createRuntimeCall(assertFunc, nil, "")
|
||||
b.CreateUnreachable()
|
||||
b.SetInsertPointAtEnd(savedBlock)
|
||||
return block
|
||||
|
||||
// Ok: assert didn't trigger so continue normally.
|
||||
b.SetInsertPointAtEnd(nextBlock)
|
||||
}
|
||||
|
||||
// extendInteger extends the value to at least targetType using a zero or sign
|
||||
|
||||
@@ -35,9 +35,6 @@ const (
|
||||
|
||||
// Whether this is a readonly parameter (for example, a string pointer).
|
||||
paramIsReadonly
|
||||
|
||||
// Whether this parameter is passed through backing storage.
|
||||
paramIsIndirect
|
||||
)
|
||||
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
@@ -105,18 +102,6 @@ func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Valu
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
// parameter list.
|
||||
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
if c.isIndirectAggregate(t) {
|
||||
return []paramInfo{{
|
||||
llvmType: c.dataPtrType,
|
||||
name: name,
|
||||
elemSize: c.targetData.TypeAllocSize(t),
|
||||
flags: paramIsGoParam | paramIsReadonly | paramIsIndirect,
|
||||
}}
|
||||
}
|
||||
return c.expandDirectFormalParamType(t, name, goType)
|
||||
}
|
||||
|
||||
func (c *compilerContext) expandDirectFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fieldInfos := c.flattenAggregateType(t, name, goType)
|
||||
@@ -130,38 +115,6 @@ func (c *compilerContext) expandDirectFormalParamType(t llvm.Type, name string,
|
||||
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||
}
|
||||
|
||||
func (c *compilerContext) storedParamType(t llvm.Type, exported bool) llvm.Type {
|
||||
if c.isIndirectParam(t, exported) {
|
||||
return c.dataPtrType
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (c *compilerContext) isIndirectParam(t llvm.Type, exported bool) bool {
|
||||
return !exported && c.isIndirectAggregate(t)
|
||||
}
|
||||
|
||||
func (b *builder) appendStoredValueTypes(valueTypes []llvm.Type, values []ssa.Value, exported bool) []llvm.Type {
|
||||
for _, value := range values {
|
||||
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(value.Type()), exported))
|
||||
}
|
||||
return valueTypes
|
||||
}
|
||||
|
||||
func (b *builder) appendStoredParamTypes(valueTypes []llvm.Type, params []*types.Var, exported bool) []llvm.Type {
|
||||
for _, param := range params {
|
||||
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(param.Type()), exported))
|
||||
}
|
||||
return valueTypes
|
||||
}
|
||||
|
||||
func (b *builder) prependIndirectResult(sig *types.Signature, exported bool, params []llvm.Value, name string) []llvm.Value {
|
||||
if resultType, indirect := b.hasIndirectResult(sig); !exported && indirect {
|
||||
return append([]llvm.Value{b.createIndirectStorage(resultType, name)}, params...)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// expandFormalParamOffsets returns a list of offsets from the start of an
|
||||
// object of type t after it would have been split up by expandFormalParam. This
|
||||
// is useful for debug information, where it is necessary to know the offset
|
||||
|
||||
+39
-23
@@ -30,15 +30,17 @@ func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
|
||||
// actual channel send operation during goroutine lowering.
|
||||
func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
ch := b.getValue(instr.Chan, getPos(instr))
|
||||
chanValue := b.getValue(instr.X, getPos(instr))
|
||||
|
||||
// store value-to-send
|
||||
valueType := b.getLLVMType(instr.X.Type())
|
||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||
var storage valueStorage
|
||||
var valueAlloca, valueAllocaSize llvm.Value
|
||||
if isZeroSize {
|
||||
storage.ptr = llvm.ConstNull(b.dataPtrType)
|
||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||
} else {
|
||||
storage = b.getValueStorage(instr.X, "chan.value")
|
||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
b.CreateStore(chanValue, valueAlloca)
|
||||
}
|
||||
|
||||
// Allocate buffer for the channel operation.
|
||||
@@ -46,13 +48,15 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||
|
||||
// Do the send.
|
||||
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, storage.ptr, channelOpAlloca}, "")
|
||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
||||
|
||||
// End the lifetime of the allocas.
|
||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||
b.endValueStorage(storage)
|
||||
if !isZeroSize {
|
||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||
}
|
||||
}
|
||||
|
||||
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||
@@ -62,22 +66,42 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
ch := b.getValue(unop.X, getPos(unop))
|
||||
|
||||
// Allocate memory to receive into.
|
||||
result := b.createRuntimeValueResult(valueType, unop.CommaOk, true, "chan")
|
||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||
var valueAlloca, valueAllocaSize llvm.Value
|
||||
if isZeroSize {
|
||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||
} else {
|
||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
}
|
||||
|
||||
// Allocate buffer for the channel operation.
|
||||
channelOp := b.getLLVMRuntimeType("channelOp")
|
||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||
|
||||
// Do the receive.
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, result.valuePtr, channelOpAlloca}, "")
|
||||
received := result.finish(b, commaOk, "chan.received")
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
||||
var received llvm.Value
|
||||
if isZeroSize {
|
||||
received = llvm.ConstNull(valueType)
|
||||
} else {
|
||||
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||
}
|
||||
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||
return received
|
||||
|
||||
if unop.CommaOk {
|
||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
||||
tuple = b.CreateInsertValue(tuple, received, 0, "")
|
||||
tuple = b.CreateInsertValue(tuple, commaOk, 1, "")
|
||||
return tuple
|
||||
} else {
|
||||
return received
|
||||
}
|
||||
}
|
||||
|
||||
// createChanClose closes the given channel.
|
||||
func (b *builder) createChanClose(ch llvm.Value) {
|
||||
b.createRuntimeInvoke("chanClose", []llvm.Value{ch}, "")
|
||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
||||
}
|
||||
|
||||
// createSelect emits all IR necessary for a select statements. That's a
|
||||
@@ -146,7 +170,9 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
case types.SendOnly:
|
||||
// Store this value in an alloca and put a pointer to this alloca
|
||||
// in the send state.
|
||||
alloca := b.getSelectSendStorage(state.Send)
|
||||
sendValue := b.getValue(state.Send, state.Pos)
|
||||
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
|
||||
b.CreateStore(sendValue, alloca)
|
||||
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
|
||||
default:
|
||||
panic("unreachable")
|
||||
@@ -254,17 +280,7 @@ 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)]
|
||||
return b.loadFromStorage(recvbuf, expr.Type(), "select.received")
|
||||
typ := b.getLLVMType(expr.Type())
|
||||
return b.CreateLoad(typ, recvbuf, "")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) getSelectSendStorage(value ssa.Value) llvm.Value {
|
||||
typ := b.getLLVMType(value.Type())
|
||||
if b.isIndirectAggregate(typ) {
|
||||
return b.getValuePointer(value)
|
||||
}
|
||||
llvmValue := b.getValue(value, getPos(value))
|
||||
ptr := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, "select.send.value")
|
||||
b.CreateStore(llvmValue, ptr)
|
||||
return ptr
|
||||
}
|
||||
|
||||
+72
-353
@@ -90,10 +90,8 @@ type compilerContext struct {
|
||||
astComments map[string]*ast.CommentGroup
|
||||
embedGlobals map[string][]*loader.EmbedFile
|
||||
pkg *types.Package
|
||||
loaderPkg *loader.Package // current package being compiled (for AST access)
|
||||
packageDir string // directory for this package
|
||||
packageDir string // directory for this package
|
||||
runtimePkg *types.Package
|
||||
localTypeNames typeutil.Map // *types.Named (synthetic local from generic instantiation) -> string
|
||||
}
|
||||
|
||||
// newCompilerContext returns a new compiler context ready for use, most
|
||||
@@ -155,8 +153,6 @@ type builder struct {
|
||||
llvmFn llvm.Value
|
||||
info functionInfo
|
||||
locals map[ssa.Value]llvm.Value // local variables
|
||||
indirectValues map[ssa.Value]llvm.Value
|
||||
indirectReturn llvm.Value
|
||||
blockInfo []blockInfo
|
||||
currentBlock *ssa.BasicBlock
|
||||
currentBlockInfo *blockInfo
|
||||
@@ -171,7 +167,7 @@ type builder struct {
|
||||
dilocals map[*types.Var]llvm.Metadata
|
||||
initInlinedAt llvm.Metadata // fake inlinedAt position
|
||||
initPseudoFuncs map[string]llvm.Metadata // fake "inlined" functions for proper init debug locations
|
||||
allDeferFuncs []any
|
||||
allDeferFuncs []interface{}
|
||||
deferFuncs map[*ssa.Function]int
|
||||
deferInvokeFuncs map[string]int
|
||||
deferClosureFuncs map[*ssa.Function]int
|
||||
@@ -180,9 +176,6 @@ type builder struct {
|
||||
deferBuiltinFuncs map[ssa.Value]deferBuiltin
|
||||
runDefersBlock []llvm.BasicBlock
|
||||
afterDefersBlock []llvm.BasicBlock
|
||||
|
||||
runtimeAssertBlocks map[string]llvm.BasicBlock
|
||||
interfaceAssertBlock llvm.BasicBlock
|
||||
}
|
||||
|
||||
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
|
||||
@@ -195,7 +188,6 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
|
||||
llvmFn: fn,
|
||||
info: c.getFunctionInfo(f),
|
||||
locals: make(map[ssa.Value]llvm.Value),
|
||||
indirectValues: make(map[ssa.Value]llvm.Value),
|
||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||
}
|
||||
}
|
||||
@@ -302,18 +294,12 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
c.packageDir = pkg.OriginalDir()
|
||||
c.embedGlobals = pkg.EmbedGlobals
|
||||
c.pkg = pkg.Pkg
|
||||
c.loaderPkg = pkg
|
||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||
c.program = ssaPkg.Prog
|
||||
|
||||
// Convert AST to SSA.
|
||||
ssaPkg.Build()
|
||||
|
||||
// Assign names to function-local named types before compiling the
|
||||
// package, so that types declared in different functions (or in
|
||||
// different instantiations of a generic function) do not collide.
|
||||
c.scanLocalTypes(ssaPkg)
|
||||
|
||||
// Initialize debug information.
|
||||
if c.Debug {
|
||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
@@ -328,6 +314,9 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
// Load comments such as //go:extern on globals.
|
||||
c.loadASTComments(pkg)
|
||||
|
||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
||||
// functionality.
|
||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
||||
if c.NeedsStackObjects {
|
||||
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
|
||||
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
|
||||
@@ -425,19 +414,19 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||
return c.ctx.Int8Type()
|
||||
case types.Int16, types.Uint16:
|
||||
return c.ctx.Int16Type()
|
||||
case types.Int32, types.Uint32, types.UntypedRune:
|
||||
case types.Int32, types.Uint32:
|
||||
return c.ctx.Int32Type()
|
||||
case types.Int, types.Uint, types.UntypedInt:
|
||||
case types.Int, types.Uint:
|
||||
return c.intType
|
||||
case types.Int64, types.Uint64:
|
||||
return c.ctx.Int64Type()
|
||||
case types.Float32:
|
||||
return c.ctx.FloatType()
|
||||
case types.Float64, types.UntypedFloat:
|
||||
case types.Float64:
|
||||
return c.ctx.DoubleType()
|
||||
case types.Complex64:
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)
|
||||
case types.Complex128, types.UntypedComplex:
|
||||
case types.Complex128:
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
||||
case types.String, types.UntypedString:
|
||||
return c.getLLVMRuntimeType("_string")
|
||||
@@ -873,9 +862,10 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
if ok := b.defineMathOp(); ok {
|
||||
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 ok := b.defineMathBitsIntrinsic(); ok {
|
||||
@@ -883,10 +873,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
// with a LLVM intrinsic.
|
||||
continue
|
||||
}
|
||||
if ok := b.defineCryptoIntrinsic(); ok {
|
||||
// Body of this function was replaced
|
||||
continue
|
||||
}
|
||||
if member.Blocks == nil {
|
||||
// Try to define this as an intrinsic function.
|
||||
b.defineIntrinsicFunction()
|
||||
@@ -1285,28 +1271,10 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
||||
|
||||
// Load function parameters
|
||||
llvmParamIndex := 0
|
||||
if _, indirectResult := b.hasIndirectResult(b.fn.Signature); indirectResult && !b.info.exported {
|
||||
b.indirectReturn = b.llvmFn.Param(llvmParamIndex)
|
||||
b.indirectReturn.SetName("return")
|
||||
llvmParamIndex++
|
||||
}
|
||||
for _, param := range b.fn.Params {
|
||||
llvmType := b.getLLVMType(param.Type())
|
||||
if b.isIndirectParam(llvmType, b.info.exported) {
|
||||
llvmParam := b.llvmFn.Param(llvmParamIndex)
|
||||
llvmParam.SetName(param.Name())
|
||||
b.indirectValues[param] = llvmParam
|
||||
llvmParamIndex++
|
||||
continue
|
||||
}
|
||||
var paramInfos []paramInfo
|
||||
if b.info.exported {
|
||||
paramInfos = b.expandDirectFormalParamType(llvmType, param.Name(), param.Type())
|
||||
} else {
|
||||
paramInfos = b.expandFormalParamType(llvmType, param.Name(), param.Type())
|
||||
}
|
||||
fields := make([]llvm.Value, 0, 1)
|
||||
for _, info := range paramInfos {
|
||||
for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
||||
param := b.llvmFn.Param(llvmParamIndex)
|
||||
param.SetName(info.name)
|
||||
fields = append(fields, param)
|
||||
@@ -1444,7 +1412,7 @@ func (b *builder) createFunction() {
|
||||
for _, phi := range b.phis {
|
||||
block := phi.ssa.Block()
|
||||
for i, edge := range phi.ssa.Edges {
|
||||
llvmVal := b.getCallArgument(edge, false)
|
||||
llvmVal := b.getValue(edge, getPos(phi.ssa))
|
||||
llvmBlock := b.blockInfo[block.Preds[i].Index].exit
|
||||
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
|
||||
}
|
||||
@@ -1453,9 +1421,6 @@ func (b *builder) createFunction() {
|
||||
if b.NeedsStackObjects {
|
||||
// Track phi nodes.
|
||||
for _, phi := range b.phis {
|
||||
if b.isOversizedAggregate(phi.ssa.Type()) {
|
||||
continue
|
||||
}
|
||||
insertPoint := llvm.NextInstruction(phi.llvm)
|
||||
for !insertPoint.IsAPHINode().IsNil() {
|
||||
insertPoint = llvm.NextInstruction(insertPoint)
|
||||
@@ -1547,7 +1512,10 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
b.diagnostics = append(b.diagnostics, err)
|
||||
b.locals[instr] = llvm.Undef(b.getLLVMType(instr.Type()))
|
||||
} else {
|
||||
b.setValue(instr, value)
|
||||
b.locals[instr] = value
|
||||
if len(*instr.Referrers()) != 0 && b.NeedsStackObjects {
|
||||
b.trackExpr(instr, value)
|
||||
}
|
||||
}
|
||||
case *ssa.DebugRef:
|
||||
// ignore
|
||||
@@ -1567,8 +1535,10 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
b.CreateBr(blockJump)
|
||||
case *ssa.MapUpdate:
|
||||
m := b.getValue(instr.Map, getPos(instr))
|
||||
key := b.getValue(instr.Key, getPos(instr))
|
||||
value := b.getValue(instr.Value, getPos(instr))
|
||||
mapType := instr.Map.Type().Underlying().(*types.Map)
|
||||
b.createMapUpdate(mapType.Key(), m, instr.Key, instr.Value, instr.Pos())
|
||||
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
|
||||
case *ssa.Panic:
|
||||
value := b.getValue(instr.X, getPos(instr))
|
||||
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
|
||||
@@ -1577,7 +1547,19 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
if b.hasDeferFrame() {
|
||||
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
|
||||
}
|
||||
b.createReturn(instr.Results, getPos(instr))
|
||||
if len(instr.Results) == 0 {
|
||||
b.CreateRetVoid()
|
||||
} else if len(instr.Results) == 1 {
|
||||
b.CreateRet(b.getValue(instr.Results[0], getPos(instr)))
|
||||
} else {
|
||||
// Multiple return values. Put them all in a struct.
|
||||
retVal := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
|
||||
for i, result := range instr.Results {
|
||||
val := b.getValue(result, getPos(instr))
|
||||
retVal = b.CreateInsertValue(retVal, val, i, "")
|
||||
}
|
||||
b.CreateRet(retVal)
|
||||
}
|
||||
case *ssa.RunDefers:
|
||||
// Note where we're going to put the rundefers block
|
||||
run := b.insertBasicBlock("rundefers.block")
|
||||
@@ -1591,246 +1573,18 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
b.createChanSend(instr)
|
||||
case *ssa.Store:
|
||||
llvmAddr := b.getValue(instr.Addr, getPos(instr))
|
||||
llvmVal := b.getValue(instr.Val, getPos(instr))
|
||||
b.createNilCheck(instr.Addr, llvmAddr, "store")
|
||||
llvmType := b.getLLVMType(instr.Val.Type())
|
||||
if b.targetData.TypeAllocSize(llvmType) == 0 {
|
||||
if b.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
|
||||
// nothing to store
|
||||
return
|
||||
}
|
||||
b.storeValue(llvmAddr, instr.Val)
|
||||
b.CreateStore(llvmVal, llvmAddr)
|
||||
default:
|
||||
b.addError(instr.Pos(), "unknown instruction: "+instr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) setValue(value ssa.Value, llvmValue llvm.Value) {
|
||||
if b.isAggregateValue(value.Type()) && !llvmValue.IsNil() && llvmValue.Type().TypeKind() == llvm.PointerTypeKind {
|
||||
b.indirectValues[value] = llvmValue
|
||||
return
|
||||
}
|
||||
b.locals[value] = llvmValue
|
||||
if len(*value.Referrers()) != 0 && b.NeedsStackObjects {
|
||||
b.trackExpr(value, llvmValue)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) createReturn(results []ssa.Value, pos token.Pos) {
|
||||
if len(results) == 0 {
|
||||
b.CreateRetVoid()
|
||||
} else if !b.indirectReturn.IsNil() {
|
||||
if len(results) == 1 {
|
||||
b.storeValue(b.indirectReturn, results[0])
|
||||
} else {
|
||||
returnType := b.getLLVMResultType(b.fn.Signature)
|
||||
for i, result := range results {
|
||||
fieldPtr := b.CreateStructGEP(returnType, b.indirectReturn, i, "")
|
||||
b.storeValue(fieldPtr, result)
|
||||
}
|
||||
}
|
||||
b.CreateRetVoid()
|
||||
} else if len(results) == 1 {
|
||||
b.CreateRet(b.getValue(results[0], pos))
|
||||
} else {
|
||||
result := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
|
||||
for i, value := range results {
|
||||
result = b.CreateInsertValue(result, b.getValue(value, pos), i, "")
|
||||
}
|
||||
b.CreateRet(result)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) isOversizedAggregate(typ types.Type) bool {
|
||||
if !b.isAggregateValue(typ) {
|
||||
return false
|
||||
}
|
||||
return b.isIndirectAggregate(b.getLLVMType(typ))
|
||||
}
|
||||
|
||||
func (b *builder) isAggregateValue(typ types.Type) bool {
|
||||
if tuple, ok := typ.(*types.Tuple); ok {
|
||||
for i := 0; i < tuple.Len(); i++ {
|
||||
if !isLLVMValueType(tuple.At(i).Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch typ.Underlying().(type) {
|
||||
case *types.Array, *types.Struct:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
if !isLLVMValueType(typ) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *builder) getValuePointer(value ssa.Value) llvm.Value {
|
||||
if ptr, ok := b.indirectValues[value]; ok {
|
||||
return ptr
|
||||
}
|
||||
llvmType := b.getLLVMType(value.Type())
|
||||
ptr := b.createIndirectStorage(llvmType, value.Name())
|
||||
b.storeValue(ptr, value)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b *builder) getCallArgument(value ssa.Value, exported bool) llvm.Value {
|
||||
paramType := b.getLLVMType(value.Type())
|
||||
if b.isIndirectParam(paramType, exported) {
|
||||
return b.getValuePointer(value)
|
||||
}
|
||||
return b.getValue(value, getPos(value))
|
||||
}
|
||||
|
||||
func (b *builder) createIndirectStorage(typ llvm.Type, name string) llvm.Value {
|
||||
// Use runtime.alloc here so storage that escapes remains valid. The
|
||||
// allocation optimizer moves bounded non-escaping storage to the stack.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false)
|
||||
layout := b.createObjectLayout(typ, b.fn.Pos())
|
||||
ptr := b.createAlloc(size, layout, b.targetData.ABITypeAlignment(typ), name)
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(ptr)
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b *builder) copyIndirectAggregate(dst, src llvm.Value, typ llvm.Type) {
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false)
|
||||
b.createMemCopy("memcpy", dst, src, size)
|
||||
}
|
||||
|
||||
func (b *builder) storeValue(dst llvm.Value, value ssa.Value) {
|
||||
typ := b.getLLVMType(value.Type())
|
||||
if src, ok := b.indirectValues[value]; ok {
|
||||
b.copyIndirectAggregate(dst, src, typ)
|
||||
} else {
|
||||
b.CreateStore(b.getValue(value, getPos(value)), dst)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) copyToIndirectStorage(src llvm.Value, typ llvm.Type, name string) llvm.Value {
|
||||
dst := b.createIndirectStorage(typ, name)
|
||||
b.copyIndirectAggregate(dst, src, typ)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (b *builder) loadFromStorage(ptr llvm.Value, typ types.Type, name string) llvm.Value {
|
||||
llvmType := b.getLLVMType(typ)
|
||||
if b.isIndirectAggregate(llvmType) {
|
||||
return b.copyToIndirectStorage(ptr, llvmType, name)
|
||||
}
|
||||
return b.CreateLoad(llvmType, ptr, name)
|
||||
}
|
||||
|
||||
func (b *builder) getValueField(value ssa.Value, index int, resultType types.Type, name string) (llvm.Value, bool) {
|
||||
if !b.isAggregateValue(value.Type()) {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
valueType := b.getLLVMType(value.Type())
|
||||
if _, indirect := b.indirectValues[value]; !indirect && !b.isIndirectAggregate(valueType) {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
fieldPtr := b.CreateStructGEP(valueType, b.getValuePointer(value), index, "")
|
||||
return b.loadFromStorage(fieldPtr, resultType, name), true
|
||||
}
|
||||
|
||||
func (b *builder) zeroIndirectStorage(ptr llvm.Value, typ llvm.Type) {
|
||||
memset := b.getMemsetFunc()
|
||||
b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
|
||||
ptr,
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||
llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false),
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
}
|
||||
|
||||
type valueStorage struct {
|
||||
ptr, size llvm.Value
|
||||
temporary bool
|
||||
}
|
||||
|
||||
func (b *builder) getValueStorage(value ssa.Value, name string) valueStorage {
|
||||
typ := b.getLLVMType(value.Type())
|
||||
if b.isIndirectAggregate(typ) {
|
||||
return valueStorage{ptr: b.getValuePointer(value)}
|
||||
}
|
||||
ptr, size := b.createTemporaryAlloca(typ, name)
|
||||
b.storeValue(ptr, value)
|
||||
return valueStorage{ptr: ptr, size: size, temporary: true}
|
||||
}
|
||||
|
||||
func (b *builder) endValueStorage(storage valueStorage) {
|
||||
if storage.temporary {
|
||||
b.emitLifetimeEnd(storage.ptr, storage.size)
|
||||
}
|
||||
}
|
||||
|
||||
type runtimeValueResult struct {
|
||||
valueType llvm.Type
|
||||
resultType llvm.Type
|
||||
result llvm.Value
|
||||
valuePtr llvm.Value
|
||||
valueSize llvm.Value
|
||||
temporary bool
|
||||
zero bool
|
||||
commaOk bool
|
||||
}
|
||||
|
||||
func (b *builder) createRuntimeValueResult(valueType llvm.Type, commaOk, zeroAsNull bool, name string) runtimeValueResult {
|
||||
result := runtimeValueResult{
|
||||
valueType: valueType,
|
||||
resultType: valueType,
|
||||
valueSize: llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(valueType), false),
|
||||
commaOk: commaOk,
|
||||
}
|
||||
if commaOk {
|
||||
result.resultType = b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false)
|
||||
}
|
||||
if b.isIndirectAggregate(result.resultType) {
|
||||
result.result = b.createIndirectStorage(result.resultType, name+".result")
|
||||
result.valuePtr = result.result
|
||||
if commaOk {
|
||||
result.valuePtr = b.CreateStructGEP(result.resultType, result.result, 0, "")
|
||||
}
|
||||
return result
|
||||
}
|
||||
if zeroAsNull && b.targetData.TypeAllocSize(valueType) == 0 {
|
||||
result.valuePtr = llvm.ConstNull(b.dataPtrType)
|
||||
result.zero = true
|
||||
return result
|
||||
}
|
||||
result.valuePtr, result.valueSize = b.createTemporaryAlloca(valueType, name+".value")
|
||||
result.temporary = true
|
||||
return result
|
||||
}
|
||||
|
||||
func (r runtimeValueResult) finish(b *builder, commaOk llvm.Value, name string) llvm.Value {
|
||||
if !r.result.IsNil() {
|
||||
if r.commaOk {
|
||||
b.CreateStore(commaOk, b.CreateStructGEP(r.resultType, r.result, 1, ""))
|
||||
}
|
||||
return r.result
|
||||
}
|
||||
|
||||
var value llvm.Value
|
||||
if r.zero {
|
||||
value = llvm.ConstNull(r.valueType)
|
||||
} else {
|
||||
value = b.CreateLoad(r.valueType, r.valuePtr, name)
|
||||
}
|
||||
if r.temporary {
|
||||
b.emitLifetimeEnd(r.valuePtr, r.valueSize)
|
||||
}
|
||||
if !r.commaOk {
|
||||
return value
|
||||
}
|
||||
result := llvm.Undef(r.resultType)
|
||||
result = b.CreateInsertValue(result, value, 0, "")
|
||||
return b.CreateInsertValue(result, commaOk, 1, "")
|
||||
}
|
||||
|
||||
// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
|
||||
// LLVM IR. It uses runtime calls for some builtins.
|
||||
func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, callName string, pos token.Pos) (llvm.Value, error) {
|
||||
@@ -2130,12 +1884,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
// not of the current function.
|
||||
useParentFrame = 1
|
||||
}
|
||||
// Prevent inlining of functions that call recover(), matching the
|
||||
// Go compiler's behavior. If this function were inlined into a
|
||||
// deferred function, recover() would incorrectly succeed because
|
||||
// the inlined code runs in the deferred function's context.
|
||||
noinline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0)
|
||||
b.llvmFn.AddFunctionAttr(noinline)
|
||||
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
|
||||
case "ssa:wrapnilchk":
|
||||
// TODO: do an actual nil check?
|
||||
@@ -2216,7 +1964,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
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 := b.getFunctionInfo(fn).linkName
|
||||
name := fn.RelString(nil)
|
||||
switch {
|
||||
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)
|
||||
@@ -2228,14 +1976,10 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.emitSV64Call(instr.Args, getPos(instr))
|
||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||
return b.emitCSROperation(instr)
|
||||
case (strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall")) && name != "syscall.SyscallN":
|
||||
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall"):
|
||||
if b.GOOS != "darwin" {
|
||||
return b.createSyscall(instr)
|
||||
}
|
||||
case name == "syscall.syscalln":
|
||||
if b.GOOS == "windows" {
|
||||
return b.createSyscalln(instr)
|
||||
}
|
||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
|
||||
return b.createRawSyscallNoError(instr)
|
||||
case name == "runtime.supportsRecover":
|
||||
@@ -2265,10 +2009,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
}
|
||||
}
|
||||
|
||||
var params []llvm.Value
|
||||
for _, param := range instr.Args {
|
||||
params = append(params, b.getValue(param, getPos(instr)))
|
||||
}
|
||||
|
||||
// Try to call the function directly for trivially static calls.
|
||||
var callee, context llvm.Value
|
||||
var calleeType llvm.Type
|
||||
var invokeTypecode, invokeReceiver llvm.Value
|
||||
exported := false
|
||||
if fn := instr.StaticCallee(); fn != nil {
|
||||
calleeType, callee = b.getFunction(fn)
|
||||
@@ -2298,18 +2046,19 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
exported = info.exported
|
||||
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
||||
// Builtin function (append, close, delete, etc.).)
|
||||
var params []llvm.Value
|
||||
var argTypes []types.Type
|
||||
for _, arg := range instr.Args {
|
||||
argTypes = append(argTypes, arg.Type())
|
||||
params = append(params, b.getValue(arg, getPos(instr)))
|
||||
}
|
||||
return b.createBuiltin(argTypes, params, call.Name(), instr.Pos())
|
||||
} else if instr.IsInvoke() {
|
||||
// Interface method call (aka invoke call).
|
||||
itf := b.getValue(instr.Value, getPos(instr)) // interface value (runtime._interface)
|
||||
invokeTypecode = b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||
invokeReceiver = b.CreateExtractValue(itf, 1, "invoke.func.value")
|
||||
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||
value := b.CreateExtractValue(itf, 1, "invoke.func.value") // receiver
|
||||
// Prefix the params with receiver value and suffix with typecode.
|
||||
params = append([]llvm.Value{value}, params...)
|
||||
params = append(params, typecode)
|
||||
callee = b.getInvokeFunction(instr)
|
||||
calleeType = callee.GlobalValueType()
|
||||
context = llvm.Undef(b.dataPtrType)
|
||||
@@ -2323,23 +2072,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
b.createNilCheck(instr.Value, callee, "fpcall")
|
||||
}
|
||||
|
||||
var params []llvm.Value
|
||||
for _, param := range instr.Args {
|
||||
params = append(params, b.getCallArgument(param, exported))
|
||||
}
|
||||
if instr.IsInvoke() {
|
||||
params = append([]llvm.Value{invokeReceiver}, params...)
|
||||
params = append(params, invokeTypecode)
|
||||
}
|
||||
|
||||
if !exported {
|
||||
if resultType, indirectResult := b.hasIndirectResult(instr.Signature()); indirectResult {
|
||||
result := b.createIndirectStorage(resultType, "call.result")
|
||||
params = append([]llvm.Value{result}, params...)
|
||||
params = append(params, context)
|
||||
b.createInvoke(calleeType, callee, params, "")
|
||||
return result, nil
|
||||
}
|
||||
// This function takes a context parameter.
|
||||
// Add it to the end of the parameter list.
|
||||
params = append(params, context)
|
||||
@@ -2378,9 +2111,6 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
|
||||
return value
|
||||
default:
|
||||
// other (local) SSA value
|
||||
if value, ok := b.indirectValues[expr]; ok {
|
||||
return b.CreateLoad(b.getLLVMType(expr.Type()), value, "")
|
||||
}
|
||||
if value, ok := b.locals[expr]; ok {
|
||||
return value
|
||||
} else {
|
||||
@@ -2405,10 +2135,13 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
||||
if elementSize == 0 {
|
||||
elementSize = 1
|
||||
}
|
||||
maxSize := min(
|
||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||
// an int.
|
||||
maxPointerValue/elementSize, maxIntegerValue)
|
||||
maxSize := maxPointerValue / elementSize
|
||||
|
||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||
// an int.
|
||||
if maxSize > maxIntegerValue {
|
||||
maxSize = maxIntegerValue
|
||||
}
|
||||
|
||||
return maxSize
|
||||
}
|
||||
@@ -2435,8 +2168,9 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
}
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
||||
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
||||
align := b.targetData.ABITypeAlignment(typ)
|
||||
buf := b.createAlloc(sizeValue, layoutValue, align, expr.Comment)
|
||||
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
return buf, nil
|
||||
} else {
|
||||
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
|
||||
@@ -2463,15 +2197,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
// This instruction changes the type, but the underlying value remains
|
||||
// the same. This is often a no-op, but sometimes we have to change the
|
||||
// LLVM type as well.
|
||||
llvmType := b.getLLVMType(expr.Type())
|
||||
if b.isIndirectAggregate(llvmType) {
|
||||
sourceType := b.getLLVMType(expr.X.Type())
|
||||
if !b.isIndirectAggregate(sourceType) || b.targetData.TypeAllocSize(sourceType) != b.targetData.TypeAllocSize(llvmType) {
|
||||
return llvm.Value{}, errors.New("todo: indirect aggregate ChangeType with different layout")
|
||||
}
|
||||
return b.getValuePointer(expr.X), nil
|
||||
}
|
||||
x := b.getValue(expr.X, getPos(expr))
|
||||
llvmType := b.getLLVMType(expr.Type())
|
||||
if x.Type() == llvmType {
|
||||
// Different Go type but same LLVM type (for example, named int).
|
||||
// This is the common case.
|
||||
@@ -2501,15 +2228,9 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
if _, ok := expr.Tuple.(*ssa.Select); ok {
|
||||
return b.getChanSelectResult(expr), nil
|
||||
}
|
||||
if value, ok := b.getValueField(expr.Tuple, expr.Index, expr.Type(), expr.Name()); ok {
|
||||
return value, nil
|
||||
}
|
||||
value := b.getValue(expr.Tuple, getPos(expr))
|
||||
return b.CreateExtractValue(value, expr.Index, ""), nil
|
||||
case *ssa.Field:
|
||||
if value, ok := b.getValueField(expr.X, expr.Field, expr.Type(), expr.Name()); ok {
|
||||
return value, nil
|
||||
}
|
||||
value := b.getValue(expr.X, getPos(expr))
|
||||
result := b.CreateExtractValue(value, expr.Field, "")
|
||||
return result, nil
|
||||
@@ -2532,11 +2253,11 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
case *ssa.Global:
|
||||
panic("global is not an expression")
|
||||
case *ssa.Index:
|
||||
collection := b.getValue(expr.X, getPos(expr))
|
||||
index := b.getValue(expr.Index, getPos(expr))
|
||||
|
||||
switch xType := expr.X.Type().Underlying().(type) {
|
||||
case *types.Basic: // extract byte from string
|
||||
collection := b.getValue(expr.X, getPos(expr))
|
||||
// Value type must be a string, which is a basic type.
|
||||
if xType.Info()&types.IsString == 0 {
|
||||
panic("lookup on non-string?")
|
||||
@@ -2570,12 +2291,13 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
|
||||
// Can't load directly from array (as index is non-constant), so
|
||||
// have to do it using an alloca+gep+load.
|
||||
arrayType := b.getLLVMType(expr.X.Type())
|
||||
storage := b.getValueStorage(expr.X, "index.alloca")
|
||||
arrayType := collection.Type()
|
||||
alloca, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
|
||||
b.CreateStore(collection, alloca)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
ptr := b.CreateInBoundsGEP(arrayType, storage.ptr, []llvm.Value{zero, index}, "index.gep")
|
||||
result := b.loadFromStorage(ptr, expr.Type(), "index.load")
|
||||
b.endValueStorage(storage)
|
||||
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
|
||||
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
|
||||
b.emitLifetimeEnd(alloca, allocaSize)
|
||||
return result, nil
|
||||
default:
|
||||
panic("unknown *ssa.Index type")
|
||||
@@ -2634,21 +2356,17 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
}
|
||||
case *ssa.Lookup: // map lookup
|
||||
value := b.getValue(expr.X, getPos(expr))
|
||||
index := b.getValue(expr.Index, getPos(expr))
|
||||
valueType := expr.Type()
|
||||
if expr.CommaOk {
|
||||
valueType = valueType.(*types.Tuple).At(0).Type()
|
||||
}
|
||||
return b.createMapLookup(expr.X.Type().Underlying().(*types.Map).Key(), valueType, value, expr.Index, expr.CommaOk, expr.Pos())
|
||||
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:
|
||||
return b.parseMakeClosure(expr)
|
||||
case *ssa.MakeInterface:
|
||||
if b.isOversizedAggregate(expr.X.Type()) {
|
||||
typ := b.getLLVMType(expr.X.Type())
|
||||
ptr := b.copyToIndirectStorage(b.getValuePointer(expr.X), typ, "interface.value")
|
||||
return b.createMakeInterfaceFromPointer(ptr, expr.X.Type()), nil
|
||||
}
|
||||
val := b.getValue(expr.X, getPos(expr))
|
||||
return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
||||
case *ssa.MakeMap:
|
||||
@@ -2682,7 +2400,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
}
|
||||
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
||||
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
||||
slicePtr := b.createAlloc(sliceSize, layoutValue, elemAlign, "makeslice.buf")
|
||||
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
||||
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
|
||||
|
||||
// Extend or truncate if necessary. This is safe as we've already done
|
||||
// the bounds check.
|
||||
@@ -2715,8 +2434,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
return b.createMapIteratorNext(rangeVal, llvmRangeVal, it), nil
|
||||
}
|
||||
case *ssa.Phi:
|
||||
phiType := b.storedParamType(b.getLLVMType(expr.Type()), false)
|
||||
phi := b.CreatePHI(phiType, "")
|
||||
phi := b.CreatePHI(b.getLLVMType(expr.Type()), "")
|
||||
b.phis = append(b.phis, phiNode{expr, phi})
|
||||
return phi, nil
|
||||
case *ssa.Range:
|
||||
@@ -3719,7 +3437,8 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
||||
return fn, nil
|
||||
} else {
|
||||
b.createNilCheck(unop.X, x, "deref")
|
||||
return b.loadFromStorage(x, unop.Type(), ""), nil
|
||||
load := b.CreateLoad(valueType, x, "")
|
||||
return load, nil
|
||||
}
|
||||
case token.XOR: // ^x, toggle all bits in integer
|
||||
return b.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil
|
||||
|
||||
+18
-175
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"go/types"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -51,8 +50,6 @@ func TestCompiler(t *testing.T) {
|
||||
{"channel.go", "", ""},
|
||||
{"gc.go", "", ""},
|
||||
{"zeromap.go", "", ""},
|
||||
{"generics.go", "", ""},
|
||||
{"large.go", "", ""},
|
||||
}
|
||||
if goMinor >= 20 {
|
||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||
@@ -60,9 +57,6 @@ func TestCompiler(t *testing.T) {
|
||||
if goMinor >= 21 {
|
||||
tests = append(tests, testCase{"go1.21.go", "", ""})
|
||||
}
|
||||
if goMinor >= 27 {
|
||||
tests = append(tests, testCase{"go1.27.go", "", ""})
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
name := tc.file
|
||||
@@ -127,138 +121,30 @@ func TestCompiler(t *testing.T) {
|
||||
t.Fatal("failed to read golden file:", err)
|
||||
}
|
||||
|
||||
if diff := diffIR(string(expected), mod.String()); diff != "" {
|
||||
t.Errorf("output does not match expected output (re-run with -update to regenerate):\n%s", diff)
|
||||
if !fuzzyEqualIR(mod.String(), string(expected)) {
|
||||
t.Errorf("output does not match expected output:\n%s", mod.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizedLargeAggregateABI(t *testing.T) {
|
||||
options := &compileopts.Options{Target: "wasm"}
|
||||
mod, errs := testCompilePackage(t, options, "large-optimized.go")
|
||||
if len(errs) != 0 {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
}
|
||||
return
|
||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
||||
// equal. That means, only relevant lines are compared (excluding comments
|
||||
// etc.).
|
||||
func fuzzyEqualIR(s1, s2 string) bool {
|
||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
||||
if len(lines1) != len(lines2) {
|
||||
return false
|
||||
}
|
||||
defer mod.Dispose()
|
||||
|
||||
passOptions := llvm.NewPassBuilderOptions()
|
||||
defer passOptions.Dispose()
|
||||
if err := mod.RunPasses("default<O2>", llvm.TargetMachine{}, passOptions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resultFn := mod.NamedFunction("main.makeLargeOptimizedValue")
|
||||
if resultFn.IsNil() {
|
||||
t.Fatal("missing function main.makeLargeOptimizedValue")
|
||||
}
|
||||
if resultType := resultFn.GlobalValueType().ReturnType(); resultType.TypeKind() != llvm.VoidTypeKind {
|
||||
t.Errorf("large aggregate result was promoted to %s", resultType)
|
||||
}
|
||||
|
||||
for _, name := range []string{
|
||||
"main.makeLargeOptimizedValue",
|
||||
"main.readLargeOptimizedValue",
|
||||
"main.readMixedLargeOptimizedValue",
|
||||
} {
|
||||
fn := mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
t.Fatalf("missing function %s", name)
|
||||
}
|
||||
if paramType := fn.GlobalValueType().ParamTypes()[0]; paramType.TypeKind() != llvm.PointerTypeKind {
|
||||
t.Errorf("%s aggregate parameter was promoted to %s", name, paramType)
|
||||
for i, line1 := range lines1 {
|
||||
line2 := lines2[i]
|
||||
if line1 != line2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeIR canonicalizes LLVM IR so a single golden file keeps matching
|
||||
// across LLVM versions. Golden files are written against LLVM <21; newer LLVM
|
||||
// prints some attributes differently.
|
||||
func normalizeIR(s string) string {
|
||||
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
||||
// which LLVM printed before any co-occurring attribute such as
|
||||
// 'readonly' (e.g. "ptr nocapture readonly"). LLVM 21+ prints the
|
||||
// equivalent 'captures(none)' instead, and after such attributes (e.g.
|
||||
// "ptr readonly captures(none)"). Normalize both name and position back
|
||||
// to the old spelling.
|
||||
s = normalizeCapturesAttr(s)
|
||||
|
||||
// LLVM 21+ also added an explicit 'nocreateundeforpoison' attribute to
|
||||
// certain intrinsic declarations (e.g. llvm.umin) that were implicitly
|
||||
// assumed not to create undef/poison before. It's unrelated to the
|
||||
// behavior under test, so ignore it for comparison.
|
||||
s = strings.ReplaceAll(s, "nocreateundeforpoison ", "")
|
||||
|
||||
// LLVM 22 dropped the (redundant) i64 size argument from
|
||||
// llvm.lifetime.start/end. Normalize away that argument so golden files
|
||||
// written against the two-argument form still match.
|
||||
s = lifetimeSizeArgRe.ReplaceAllString(s, "$1")
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// diffIR compares two LLVM IR strings, ignoring irrelevant lines (comments,
|
||||
// empty lines, etc.) and normalizing LLVM-version-specific spellings via
|
||||
// normalizeIR. It returns "" when they are equal. Otherwise it returns a
|
||||
// compact diff of only the region that differs: the common prefix and suffix
|
||||
// are trimmed, then the differing expected lines (prefixed "-") are shown
|
||||
// followed by the differing actual lines (prefixed "+").
|
||||
func diffIR(expected, actual string) string {
|
||||
exp := filterIrrelevantIRLines(strings.Split(normalizeIR(expected), "\n"))
|
||||
act := filterIrrelevantIRLines(strings.Split(normalizeIR(actual), "\n"))
|
||||
|
||||
// Trim the common prefix.
|
||||
start := 0
|
||||
for start < len(exp) && start < len(act) && exp[start] == act[start] {
|
||||
start++
|
||||
}
|
||||
// Trim the common suffix.
|
||||
e, a := len(exp), len(act)
|
||||
for e > start && a > start && exp[e-1] == act[a-1] {
|
||||
e--
|
||||
a--
|
||||
}
|
||||
if start == e && start == a {
|
||||
return "" // equal
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("first difference at relevant line ")
|
||||
b.WriteString(strconv.Itoa(start + 1))
|
||||
b.WriteString(":\n")
|
||||
for _, line := range exp[start:e] {
|
||||
b.WriteString("- ")
|
||||
b.WriteString(line)
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
for _, line := range act[start:a] {
|
||||
b.WriteString("+ ")
|
||||
b.WriteString(line)
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// capturesNoneAttrRe matches a co-occurring attribute directly followed by
|
||||
// 'captures(none)', which is how LLVM 21+ orders these two attributes when
|
||||
// printing IR (the pre-LLVM21 'nocapture' attribute printed the other way
|
||||
// around).
|
||||
var capturesNoneAttrRe = regexp.MustCompile(`\b(readonly|readnone|writeonly|nonnull)\s+captures\(none\)`)
|
||||
|
||||
// lifetimeSizeArgRe matches the i64 size argument of an
|
||||
// llvm.lifetime.start/end call or declaration, which LLVM 22 removed.
|
||||
var lifetimeSizeArgRe = regexp.MustCompile(`(@llvm\.lifetime\.(?:start|end)\.p0\()i64(?: immarg| \d+), `)
|
||||
|
||||
// normalizeCapturesAttr rewrites LLVM 21+'s 'captures(none)' attribute back
|
||||
// to the pre-LLVM21 'nocapture' spelling and position, so golden IR files
|
||||
// written against LLVM <21 keep matching.
|
||||
func normalizeCapturesAttr(s string) string {
|
||||
s = capturesNoneAttrRe.ReplaceAllString(s, "nocapture $1")
|
||||
s = strings.ReplaceAll(s, "captures(none)", "nocapture")
|
||||
return s
|
||||
return true
|
||||
}
|
||||
|
||||
// filterIrrelevantIRLines removes lines from the input slice of strings that
|
||||
@@ -301,9 +187,9 @@ func TestCompilerErrors(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
errorsFileString := strings.ReplaceAll(string(errorsFile), "\r\n", "\n")
|
||||
for line := range strings.SplitSeq(errorsFileString, "\n") {
|
||||
if after, ok := strings.CutPrefix(line, "// ERROR: "); ok {
|
||||
expectedErrors = append(expectedErrors, after)
|
||||
for _, line := range strings.Split(errorsFileString, "\n") {
|
||||
if strings.HasPrefix(line, "// ERROR: ") {
|
||||
expectedErrors = append(expectedErrors, strings.TrimPrefix(line, "// ERROR: "))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,49 +213,6 @@ func TestCompilerErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateValueCount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
|
||||
byteType := ctx.Int8Type()
|
||||
tests := []struct {
|
||||
name string
|
||||
typ llvm.Type
|
||||
count uint64
|
||||
exceeded bool
|
||||
}{
|
||||
{"empty", llvm.ArrayType(byteType, 0), 0, false},
|
||||
{"limit", llvm.ArrayType(byteType, 1024), 1024, false},
|
||||
{"over limit", llvm.ArrayType(byteType, 1025), 0, true},
|
||||
{"combined limit", ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(byteType, 512),
|
||||
llvm.ArrayType(byteType, 512),
|
||||
}, false), 1024, false},
|
||||
{"combined over limit", ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(byteType, 1000),
|
||||
llvm.ArrayType(byteType, 1000),
|
||||
}, false), 0, true},
|
||||
{"comma-ok over limit", ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(byteType, 1024),
|
||||
ctx.Int1Type(),
|
||||
}, false), 0, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
count, exceeded := aggregateValueCount(test.typ, 0)
|
||||
if exceeded != test.exceeded {
|
||||
t.Errorf("expected exceeded=%t, got %t", test.exceeded, exceeded)
|
||||
}
|
||||
if !exceeded && count != test.count {
|
||||
t.Errorf("expected count=%d, got %d", test.count, count)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Build a package given a number of compiler options and a file.
|
||||
func testCompilePackage(t *testing.T, options *compileopts.Options, file string) (llvm.Module, []error) {
|
||||
target, err := compileopts.LoadTarget(options)
|
||||
|
||||
+94
-118
@@ -32,7 +32,7 @@ func (b *builder) supportsRecover() bool {
|
||||
// proposal of WebAssembly:
|
||||
// https://github.com/WebAssembly/exception-handling
|
||||
return false
|
||||
case "xtensa":
|
||||
case "riscv64", "xtensa":
|
||||
// TODO: add support for these architectures
|
||||
return false
|
||||
default:
|
||||
@@ -60,6 +60,10 @@ func (b *builder) deferInitFunc() {
|
||||
b.deferExprFuncs = make(map[ssa.Value]int)
|
||||
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||
|
||||
// Create defer list pointer.
|
||||
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
||||
|
||||
if b.hasDeferFrame() {
|
||||
// Set up the defer frame with the current stack pointer.
|
||||
// This assumes that the stack pointer doesn't move outside of the
|
||||
@@ -69,22 +73,12 @@ func (b *builder) deferInitFunc() {
|
||||
// in the setjmp-like inline assembly.
|
||||
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||
// The field index must match the DeferPtr field in runtime.deferFrame,
|
||||
// defined in src/runtime/panic.go.
|
||||
b.deferPtr = b.CreateInBoundsGEP(deferFrameType, b.deferFrame, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 6, false), // DeferPtr field
|
||||
}, "deferPtr")
|
||||
stackPointer := b.readStackPointer()
|
||||
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
|
||||
|
||||
// Create the landing pad block, which is where control transfers after
|
||||
// a panic.
|
||||
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
|
||||
} else {
|
||||
// Create defer list pointer.
|
||||
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +110,8 @@ func (b *builder) createLandingPad() {
|
||||
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
||||
// Construct inline assembly equivalents of setjmp.
|
||||
// The assembly works as follows:
|
||||
// * Registers are either clobbered or, on 386, saved for longjmp to
|
||||
// restore if the ABI requires them to survive calls.
|
||||
// * 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
|
||||
@@ -130,12 +124,8 @@ func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
||||
asmString = `
|
||||
xorl %eax, %eax
|
||||
movl $$1f, 4(%ebx)
|
||||
movl %ebx, 8(%ebx)
|
||||
movl %esi, 12(%ebx)
|
||||
movl %edi, 16(%ebx)
|
||||
movl %ebp, 20(%ebx)
|
||||
1:`
|
||||
constraints = "={eax},{ebx},~{ecx},~{edx},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
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":
|
||||
@@ -217,20 +207,12 @@ sw $$ra, 4($$5)
|
||||
// So only add them when using hardfloat.
|
||||
constraints += ",~{$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}"
|
||||
}
|
||||
case "riscv32", "riscv64":
|
||||
if b.archFamily() == "riscv32" {
|
||||
asmString = `
|
||||
case "riscv32":
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
sw a2, 4(a1)
|
||||
li a0, 0
|
||||
1:`
|
||||
} else {
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
sd a2, 8(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().
|
||||
@@ -255,17 +237,6 @@ func (b *builder) createInvokeCheckpoint() {
|
||||
b.currentBlockInfo.exit = continueBB
|
||||
}
|
||||
|
||||
// createFaultCheckpoint is like createInvokeCheckpoint but for use in fault
|
||||
// blocks (e.g., bounds check failures). Unlike createInvokeCheckpoint, it does
|
||||
// not update currentBlockInfo.exit because the fault block is a dead-end that
|
||||
// does not participate in phi node resolution.
|
||||
func (b *builder) createFaultCheckpoint() {
|
||||
isZero := b.createCheckpoint(b.deferFrame)
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from the current block to itself.
|
||||
// Use Tarjan's strongly connected components algorithm to search for cycles.
|
||||
// A one-node SCC is a cycle iff there is an edge from the node to itself.
|
||||
@@ -367,44 +338,6 @@ type tarjanNode struct {
|
||||
cyclic bool
|
||||
}
|
||||
|
||||
type llvmValueList struct {
|
||||
values []llvm.Value
|
||||
types []llvm.Type
|
||||
}
|
||||
|
||||
func newLLVMValueList(values ...llvm.Value) llvmValueList {
|
||||
var list llvmValueList
|
||||
list.append(values...)
|
||||
return list
|
||||
}
|
||||
|
||||
func (l *llvmValueList) append(values ...llvm.Value) {
|
||||
for _, value := range values {
|
||||
l.values = append(l.values, value)
|
||||
l.types = append(l.types, value.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func (l *llvmValueList) appendSSAValues(values []ssa.Value, lower func(ssa.Value) llvm.Value) {
|
||||
for _, value := range values {
|
||||
l.append(lower(value))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) loadDeferredCallParams(structType llvm.Type, ptr llvm.Value) []llvm.Value {
|
||||
fieldTypes := structType.StructElementTypes()
|
||||
values := make([]llvm.Value, 0, len(fieldTypes)-2)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 2; i < len(fieldTypes); i++ {
|
||||
fieldPtr := b.CreateInBoundsGEP(structType, ptr, []llvm.Value{
|
||||
zero,
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}, "gep")
|
||||
values = append(values, b.CreateLoad(fieldTypes[i], fieldPtr, "param"))
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// createDefer emits a single defer instruction, to be run when this function
|
||||
// returns.
|
||||
func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
@@ -412,28 +345,31 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// make a linked list.
|
||||
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
|
||||
|
||||
var values llvmValueList
|
||||
lowerArgument := func(value ssa.Value) llvm.Value {
|
||||
return b.getCallArgument(value, false)
|
||||
}
|
||||
var values []llvm.Value
|
||||
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
||||
if instr.Call.IsInvoke() {
|
||||
// Method call on an interface.
|
||||
|
||||
// Get callback type number.
|
||||
key := b.getInvokeFunctionName(&instr.Call)
|
||||
if _, ok := b.deferInvokeFuncs[key]; !ok {
|
||||
b.deferInvokeFuncs[key] = len(b.allDeferFuncs)
|
||||
methodName := instr.Call.Method.FullName()
|
||||
if _, ok := b.deferInvokeFuncs[methodName]; !ok {
|
||||
b.deferInvokeFuncs[methodName] = len(b.allDeferFuncs)
|
||||
b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
|
||||
}
|
||||
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[key]), false)
|
||||
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[methodName]), false)
|
||||
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields, followed by the call parameters).
|
||||
itf := b.getValue(instr.Call.Value, getPos(instr)) // interface
|
||||
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||
values = newLLVMValueList(callback, next, typecode, receiverValue)
|
||||
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||
values = []llvm.Value{callback, next, typecode, receiverValue}
|
||||
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||
for _, arg := range instr.Call.Args {
|
||||
val := b.getValue(arg, getPos(instr))
|
||||
values = append(values, val)
|
||||
valueTypes = append(valueTypes, val.Type())
|
||||
}
|
||||
|
||||
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
|
||||
// Regular function call.
|
||||
@@ -445,11 +381,12 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields).
|
||||
values = newLLVMValueList(callback, next)
|
||||
exported := b.getFunctionInfo(callee).exported
|
||||
values.appendSSAValues(instr.Call.Args, func(value ssa.Value) llvm.Value {
|
||||
return b.getCallArgument(value, exported)
|
||||
})
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
|
||||
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
|
||||
// Immediately applied function literal with free variables.
|
||||
@@ -472,9 +409,14 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields, followed by all parameters including the
|
||||
// context pointer).
|
||||
values = newLLVMValueList(callback, next)
|
||||
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||
values.append(context)
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
values = append(values, context)
|
||||
valueTypes = append(valueTypes, context.Type())
|
||||
|
||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||
var argTypes []types.Type
|
||||
@@ -497,8 +439,11 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields).
|
||||
values = newLLVMValueList(callback, next)
|
||||
values.append(argValues...)
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range argValues {
|
||||
values = append(values, param)
|
||||
valueTypes = append(valueTypes, param.Type())
|
||||
}
|
||||
|
||||
} else {
|
||||
funcValue := b.getValue(instr.Call.Value, getPos(instr))
|
||||
@@ -513,15 +458,20 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields, followed by all parameters including the
|
||||
// context pointer).
|
||||
values = newLLVMValueList(callback, next, funcValue)
|
||||
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||
values = []llvm.Value{callback, next, funcValue}
|
||||
valueTypes = append(valueTypes, funcValue.Type())
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// Make a struct out of the collected values to put in the deferred call
|
||||
// struct.
|
||||
deferredCallType := b.ctx.StructType(values.types, false)
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCall := llvm.ConstNull(deferredCallType)
|
||||
for i, value := range values.values {
|
||||
for i, value := range values {
|
||||
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
||||
}
|
||||
|
||||
@@ -537,9 +487,8 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// This may be hit a variable number of times, so use a heap allocation.
|
||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
layoutValue := b.createObjectLayout(deferredCallType, instr.Pos())
|
||||
align := b.targetData.ABITypeAlignment(deferredCallType)
|
||||
alloca = b.createAlloc(sizeValue, layoutValue, align, "defer.alloc.call")
|
||||
nilPtr := llvm.ConstNull(b.dataPtrType)
|
||||
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||
}
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(alloca)
|
||||
@@ -623,11 +572,19 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||
}
|
||||
|
||||
valueTypes = b.appendStoredValueTypes(valueTypes, callback.Args, false)
|
||||
for _, arg := range callback.Args {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||
}
|
||||
|
||||
// Extract the params from the struct (including receiver).
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
for i := 2; i < len(valueTypes); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
|
||||
var fnPtr llvm.Value
|
||||
var fnType llvm.Type
|
||||
@@ -656,7 +613,6 @@ func (b *builder) createRunDefers() {
|
||||
// with a strict calling convention.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||
}
|
||||
forwardParams = b.prependIndirectResult(callback.Signature(), false, forwardParams, "defer.result")
|
||||
|
||||
b.createCall(fnType, fnPtr, forwardParams, "")
|
||||
|
||||
@@ -665,21 +621,27 @@ func (b *builder) createRunDefers() {
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||
exported := b.getFunctionInfo(callback).exported
|
||||
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(callback.Signature), exported)
|
||||
for _, param := range getParams(callback.Signature) {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||
}
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Extract the params from the struct.
|
||||
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := range getParams(callback.Signature) {
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
|
||||
// Plain TinyGo functions add some extra parameters to implement async functionality and function receivers.
|
||||
// These parameters should not be supplied when calling into an external C/ASM function.
|
||||
if !exported {
|
||||
if !b.getFunctionInfo(callback).exported {
|
||||
// Add the context parameter. We know it is ignored by the receiving
|
||||
// function, but we have to pass one anyway.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||
}
|
||||
forwardParams = b.prependIndirectResult(callback.Signature, exported, forwardParams, "defer.result")
|
||||
|
||||
// Call real function.
|
||||
fnType, fn := b.getFunction(callback)
|
||||
@@ -689,16 +651,24 @@ func (b *builder) createRunDefers() {
|
||||
// Get the real defer struct type and cast to it.
|
||||
fn := callback.Fn.(*ssa.Function)
|
||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(fn.Signature), false)
|
||||
params := fn.Signature.Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
valueTypes = append(valueTypes, b.dataPtrType) // closure
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Extract the params from the struct.
|
||||
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 2; i < len(valueTypes); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
|
||||
// Call deferred function.
|
||||
fnType, llvmFn := b.getFunction(fn)
|
||||
forwardParams = b.prependIndirectResult(fn.Signature, false, forwardParams, "defer.result")
|
||||
b.createCall(fnType, llvmFn, forwardParams, "")
|
||||
case *ssa.Builtin:
|
||||
db := b.deferBuiltinFuncs[callback]
|
||||
@@ -708,14 +678,20 @@ func (b *builder) createRunDefers() {
|
||||
|
||||
//Get signature from call results
|
||||
params := callback.Type().Underlying().(*types.Signature).Params()
|
||||
for v := range params.Variables() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(v.Type()))
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Extract the params from the struct.
|
||||
argValues := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
var argValues []llvm.Value
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||
argValues = append(argValues, forwardParam)
|
||||
}
|
||||
|
||||
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
|
||||
if err != nil {
|
||||
|
||||
+22
-97
@@ -10,91 +10,6 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// LLVM recursively expands each struct field and array element in parameters
|
||||
// and results into separate values. It gets very slow with too many values, so
|
||||
// pass larger aggregates indirectly before LLVM expands them.
|
||||
const maxDirectAggregateValues = 1024
|
||||
|
||||
func (c *compilerContext) getLLVMResultType(sig *types.Signature) llvm.Type {
|
||||
switch sig.Results().Len() {
|
||||
case 0:
|
||||
return c.ctx.VoidType()
|
||||
case 1:
|
||||
return c.getLLVMType(sig.Results().At(0).Type())
|
||||
default:
|
||||
results := make([]llvm.Type, sig.Results().Len())
|
||||
for i := range results {
|
||||
results[i] = c.getLLVMType(sig.Results().At(i).Type())
|
||||
}
|
||||
return c.ctx.StructType(results, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compilerContext) hasIndirectResult(sig *types.Signature) (llvm.Type, bool) {
|
||||
resultType := c.getLLVMResultType(sig)
|
||||
return resultType, c.isIndirectAggregate(resultType)
|
||||
}
|
||||
|
||||
func (c *compilerContext) isIndirectAggregate(typ llvm.Type) bool {
|
||||
switch typ.TypeKind() {
|
||||
case llvm.ArrayTypeKind, llvm.StructTypeKind:
|
||||
_, exceeded := aggregateValueCount(typ, 0)
|
||||
return exceeded
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func aggregateValueCount(typ llvm.Type, count uint64) (uint64, bool) {
|
||||
switch typ.TypeKind() {
|
||||
case llvm.ArrayTypeKind:
|
||||
length := uint64(typ.ArrayLength())
|
||||
if length == 0 {
|
||||
return count, false
|
||||
}
|
||||
elementCount, exceeded := aggregateValueCount(typ.ElementType(), 0)
|
||||
if exceeded {
|
||||
return count, true
|
||||
}
|
||||
if elementCount != 0 && length > (maxDirectAggregateValues-count)/elementCount {
|
||||
return count, true
|
||||
}
|
||||
return count + length*elementCount, false
|
||||
case llvm.StructTypeKind:
|
||||
for _, field := range typ.StructElementTypes() {
|
||||
var exceeded bool
|
||||
count, exceeded = aggregateValueCount(field, count)
|
||||
if exceeded {
|
||||
return count, true
|
||||
}
|
||||
}
|
||||
return count, false
|
||||
default:
|
||||
count++
|
||||
return count, count > maxDirectAggregateValues
|
||||
}
|
||||
}
|
||||
|
||||
func isLLVMValueType(typ types.Type) bool {
|
||||
switch typ := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return typ.Kind() != types.Invalid
|
||||
case *types.Array:
|
||||
return isLLVMValueType(typ.Elem())
|
||||
case *types.Struct:
|
||||
for field := range typ.Fields() {
|
||||
if !isLLVMValueType(field.Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// createFuncValue creates a function value from a raw function pointer with no
|
||||
// context.
|
||||
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||
@@ -133,18 +48,28 @@ func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||
|
||||
// getLLVMFunctionType returns a LLVM function type for a given signature.
|
||||
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
||||
returnType, indirectResult := c.hasIndirectResult(typ)
|
||||
// Get the return type.
|
||||
var returnType llvm.Type
|
||||
switch typ.Results().Len() {
|
||||
case 0:
|
||||
// No return values.
|
||||
returnType = c.ctx.VoidType()
|
||||
case 1:
|
||||
// Just one return value.
|
||||
returnType = c.getLLVMType(typ.Results().At(0).Type())
|
||||
default:
|
||||
// Multiple return values. Put them together in a struct.
|
||||
// This appears to be the common way to handle multiple return values in
|
||||
// LLVM.
|
||||
members := make([]llvm.Type, typ.Results().Len())
|
||||
for i := 0; i < typ.Results().Len(); i++ {
|
||||
members[i] = c.getLLVMType(typ.Results().At(i).Type())
|
||||
}
|
||||
returnType = c.ctx.StructType(members, false)
|
||||
}
|
||||
|
||||
// Get the parameter types.
|
||||
var paramTypes []llvm.Type
|
||||
if indirectResult {
|
||||
// LLVM expands aggregate returns into scalar leaves before deciding
|
||||
// whether to pass them indirectly, so a large IR return can exhaust
|
||||
// memory. Returning void avoids that expansion and cannot be demoted
|
||||
// again. Keep the result pointer first so the context remains last.
|
||||
paramTypes = append(paramTypes, c.dataPtrType)
|
||||
returnType = c.ctx.VoidType()
|
||||
}
|
||||
if typ.Recv() != nil {
|
||||
recv := c.getLLVMType(typ.Recv().Type())
|
||||
if recv.StructName() == "runtime._interface" {
|
||||
@@ -156,8 +81,8 @@ func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
}
|
||||
for v := range typ.Params().Variables() {
|
||||
subType := c.getLLVMType(v.Type())
|
||||
for i := 0; i < typ.Params().Len(); i++ {
|
||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
@@ -187,7 +112,7 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
||||
|
||||
// Store the bound variables in a single object, allocating it on the heap
|
||||
// if necessary.
|
||||
context := b.emitPointerPack(boundVars, expr.Pos())
|
||||
context := b.emitPointerPack(boundVars)
|
||||
|
||||
// Create the closure.
|
||||
_, fn := b.getFunction(f)
|
||||
|
||||
+8
-33
@@ -5,38 +5,11 @@ package compiler
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Heap-allocate a buffer of the given size. This will typically call
|
||||
// runtime.alloc.
|
||||
func (b *builder) createAlloc(sizeValue, layoutValue llvm.Value, align int, comment string) llvm.Value {
|
||||
// Normally allocate using "runtime.alloc", but use "runtime.alloc_noheap"
|
||||
// if the //go:noheap pragma is used.
|
||||
allocFunc := "alloc"
|
||||
if b.info.noheap {
|
||||
allocFunc = "alloc_noheap"
|
||||
}
|
||||
|
||||
// Allocs that don't allocate anything can return an architecture-specific
|
||||
// sentinel value.
|
||||
if !sizeValue.IsAConstantInt().IsNil() && sizeValue.ZExtValue() == 0 {
|
||||
allocFunc = "alloc_zero"
|
||||
}
|
||||
|
||||
// Make the runtime call.
|
||||
call := b.createRuntimeCall(allocFunc, []llvm.Value{sizeValue, layoutValue}, comment)
|
||||
if align == 0 || align&(align-1) != 0 {
|
||||
panic("invalid alignment")
|
||||
}
|
||||
call.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
|
||||
return call
|
||||
}
|
||||
|
||||
// trackExpr inserts pointer tracking intrinsics for the GC if the expression is
|
||||
// one of the expressions that need this.
|
||||
func (b *builder) trackExpr(expr ssa.Value, value llvm.Value) {
|
||||
@@ -89,7 +62,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
||||
return
|
||||
}
|
||||
numElements := typ.StructElementTypesCount()
|
||||
for i := range numElements {
|
||||
for i := 0; i < numElements; i++ {
|
||||
subValue := b.CreateExtractValue(value, i, "")
|
||||
b.trackValue(subValue)
|
||||
}
|
||||
@@ -98,7 +71,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
||||
return
|
||||
}
|
||||
numElements := typ.ArrayLength()
|
||||
for i := range numElements {
|
||||
for i := 0; i < numElements; i++ {
|
||||
subValue := b.CreateExtractValue(value, i, "")
|
||||
b.trackValue(subValue)
|
||||
}
|
||||
@@ -119,11 +92,13 @@ func typeHasPointers(t llvm.Type) bool {
|
||||
case llvm.PointerTypeKind:
|
||||
return true
|
||||
case llvm.StructTypeKind:
|
||||
return slices.ContainsFunc(t.StructElementTypes(), typeHasPointers)
|
||||
case llvm.ArrayTypeKind:
|
||||
if t.ArrayLength() == 0 {
|
||||
return false
|
||||
for _, subType := range t.StructElementTypes() {
|
||||
if typeHasPointers(subType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case llvm.ArrayTypeKind:
|
||||
if typeHasPointers(t.ElementType()) {
|
||||
return true
|
||||
}
|
||||
|
||||
+15
-29
@@ -47,16 +47,20 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get all function parameters to pass to the goroutine.
|
||||
var params []llvm.Value
|
||||
for _, param := range instr.Call.Args {
|
||||
params = append(params, b.expandFormalParam(b.getValue(param, getPos(instr)))...)
|
||||
}
|
||||
|
||||
var prefix string
|
||||
var funcPtr llvm.Value
|
||||
var funcType llvm.Type
|
||||
var context llvm.Value
|
||||
hasContext := false
|
||||
exported := false
|
||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||
// Static callee is known. This makes it easier to start a new
|
||||
// goroutine.
|
||||
var context llvm.Value
|
||||
switch value := instr.Call.Value.(type) {
|
||||
case *ssa.Function:
|
||||
// Goroutine call is regular function call. No context is necessary.
|
||||
@@ -69,10 +73,10 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
panic("StaticCallee returned an unexpected value")
|
||||
}
|
||||
if !context.IsNil() {
|
||||
params = append(params, context) // context parameter
|
||||
hasContext = true
|
||||
}
|
||||
funcType, funcPtr = b.getFunction(callee)
|
||||
exported = b.getFunctionInfo(callee).exported
|
||||
} else if instr.Call.IsInvoke() {
|
||||
// This is a method call on an interface value.
|
||||
itf := b.getValue(instr.Call.Value, getPos(instr))
|
||||
@@ -80,32 +84,23 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||
funcType = funcPtr.GlobalValueType()
|
||||
params = append(params, itfValue)
|
||||
context = itfTypeCode
|
||||
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
||||
params = append(params, itfTypeCode) // end with typecode
|
||||
} else {
|
||||
// This is a function pointer.
|
||||
// At the moment, two extra params are passed to the newly started
|
||||
// goroutine:
|
||||
// * The function context, for closures.
|
||||
// * The function pointer (for tasks).
|
||||
var context llvm.Value
|
||||
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)))
|
||||
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
params = append(params, context, funcPtr)
|
||||
hasContext = true
|
||||
prefix = b.getFunctionInfo(b.fn).linkName
|
||||
prefix = b.fn.RelString(nil)
|
||||
}
|
||||
|
||||
for _, param := range instr.Call.Args {
|
||||
params = append(params, b.getGoroutineCallArgument(param, exported)...)
|
||||
}
|
||||
if !context.IsNil() {
|
||||
params = append(params, context)
|
||||
}
|
||||
if hasContext && instr.Call.StaticCallee() == nil {
|
||||
params = append(params, funcPtr)
|
||||
}
|
||||
params = b.prependIndirectResult(instr.Call.Signature(), exported, params, "go.result")
|
||||
|
||||
paramBundle := b.emitPointerPack(params, instr.Pos())
|
||||
paramBundle := b.emitPointerPack(params)
|
||||
var stackSize llvm.Value
|
||||
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos())
|
||||
if b.AutomaticStackSize {
|
||||
@@ -127,15 +122,6 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
|
||||
}
|
||||
|
||||
func (b *builder) getGoroutineCallArgument(value ssa.Value, exported bool) []llvm.Value {
|
||||
typ := b.getLLVMType(value.Type())
|
||||
arg := b.getCallArgument(value, exported)
|
||||
if b.isIndirectParam(typ, exported) {
|
||||
return []llvm.Value{b.copyToIndirectStorage(arg, typ, "go.param")}
|
||||
}
|
||||
return b.expandFormalParam(arg)
|
||||
}
|
||||
|
||||
// Create an exported wrapper function for functions with the //go:wasmexport
|
||||
// pragma. This wrapper function is quite complex when the scheduler is enabled:
|
||||
// it needs to start a new goroutine each time the exported function is called.
|
||||
@@ -153,7 +139,7 @@ func (b *builder) createWasmExport() {
|
||||
// Declare the exported function.
|
||||
paramTypes := b.llvmFnType.ParamTypes()
|
||||
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
|
||||
exportedFn := llvm.AddFunction(b.mod, b.getFunctionInfo(b.fn).linkName+suffix, exportedFnType)
|
||||
exportedFn := llvm.AddFunction(b.mod, b.fn.RelString(nil)+suffix, exportedFnType)
|
||||
b.addStandardAttributes(exportedFn)
|
||||
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
|
||||
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
|
||||
@@ -428,7 +414,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
||||
// Extract parameters from the state object, and call the function
|
||||
// that's being wrapped.
|
||||
var callParams []llvm.Value
|
||||
for i := range numParams {
|
||||
for i := 0; i < numParams; i++ {
|
||||
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
|
||||
+10
-12
@@ -146,14 +146,13 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
||||
llvmArgs := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={r0}")
|
||||
constraints := "={r0}"
|
||||
for i, arg := range args[1:] {
|
||||
arg = arg.(*ssa.MakeInterface).X
|
||||
if i == 0 {
|
||||
constraints.WriteString(",0")
|
||||
constraints += ",0"
|
||||
} else {
|
||||
constraints.WriteString(",{r" + strconv.Itoa(i) + "}")
|
||||
constraints += ",{r" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
llvmValue := b.getValue(arg, pos)
|
||||
llvmArgs = append(llvmArgs, llvmValue)
|
||||
@@ -162,9 +161,9 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
||||
// Implement the ARM calling convention by marking r1-r3 as
|
||||
// clobbered. r0 is used as an output register so doesn't have to be
|
||||
// marked as clobbered.
|
||||
constraints.WriteString(",~{r1},~{r2},~{r3}")
|
||||
constraints += ",~{r1},~{r2},~{r3}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
@@ -185,14 +184,13 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
||||
llvmArgs := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={x0}")
|
||||
constraints := "={x0}"
|
||||
for i, arg := range args[1:] {
|
||||
arg = arg.(*ssa.MakeInterface).X
|
||||
if i == 0 {
|
||||
constraints.WriteString(",0")
|
||||
constraints += ",0"
|
||||
} else {
|
||||
constraints.WriteString(",{x" + strconv.Itoa(i) + "}")
|
||||
constraints += ",{x" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
llvmValue := b.getValue(arg, pos)
|
||||
llvmArgs = append(llvmArgs, llvmValue)
|
||||
@@ -201,9 +199,9 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
||||
// Implement the ARM64 calling convention by marking x1-x7 as
|
||||
// clobbered. x0 is used as an output register so doesn't have to be
|
||||
// marked as clobbered.
|
||||
constraints.WriteString(",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}")
|
||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
|
||||
+207
-582
File diff suppressed because it is too large
Load Diff
+8
-59
@@ -7,7 +7,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -167,22 +166,12 @@ func (b *builder) createMachineKeepAliveImpl() {
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
"math.Acos": "llvm.acos.f64",
|
||||
"math.Asin": "llvm.asin.f64",
|
||||
"math.Atan": "llvm.atan.f64",
|
||||
"math.Atan2": "llvm.atan2.f64",
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Cos": "llvm.cos.f64",
|
||||
"math.Cosh": "llvm.cosh.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
"math.Exp2": "llvm.exp2.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Log": "llvm.log.f64",
|
||||
"math.Sin": "llvm.sin.f64",
|
||||
"math.Sinh": "llvm.sinh.f64",
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Tan": "llvm.tan.f64",
|
||||
"math.Tanh": "llvm.tanh.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
@@ -196,40 +185,18 @@ var mathToLLVMMapping = map[string]string{
|
||||
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
||||
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||
// more expensive than 32-bit ones.
|
||||
func (b *builder) defineMathOp() bool {
|
||||
llvmName, ok := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(b.Triple, "-wasi") || llvmutil.Version() < 19 {
|
||||
// We don't have a real libc for wasip2. Until that is fixed, we need to
|
||||
// limit math intrinsics on WASI to a subset supported natively in
|
||||
// WebAssembly.
|
||||
// Also, since we don't know the specific libc we will target, disallow
|
||||
// these for all WASI targets.
|
||||
//
|
||||
// We also need to limit ourselves to LLVM 19 and above for the extended
|
||||
// set of math intrinsics, see:
|
||||
// https://discourse.llvm.org/t/rfc-all-the-math-intrinsics/78294
|
||||
switch b.fn.Name() {
|
||||
case "Ceil", "Exp", "Exp2", "Floor", "Log", "Sqrt", "Trunc":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
func (b *builder) defineMathOp() {
|
||||
b.createFunctionStart(true)
|
||||
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||
if llvmName == "" {
|
||||
panic("unreachable: unknown math operation") // sanity check
|
||||
}
|
||||
llvmFn := b.mod.NamedFunction(llvmName)
|
||||
if llvmFn.IsNil() {
|
||||
// The intrinsic doesn't exist yet, so declare it.
|
||||
var llvmType llvm.Type
|
||||
switch b.fn.Name() {
|
||||
case "Atan2":
|
||||
// double atan2(double %y, double %x)
|
||||
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)
|
||||
default:
|
||||
// double foo(double %x)
|
||||
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// Create a call to the intrinsic.
|
||||
@@ -239,24 +206,6 @@ func (b *builder) defineMathOp() bool {
|
||||
}
|
||||
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *builder) defineCryptoIntrinsic() bool {
|
||||
if b.fn.Pkg.Pkg.Path() != "crypto/internal/constanttime" {
|
||||
return false
|
||||
}
|
||||
|
||||
switch b.fn.Name() {
|
||||
case "boolToUint8":
|
||||
b.createFunctionStart(true)
|
||||
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
result := b.CreateZExt(param, b.ctx.Int8Type(), "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Implement most math/bits functions.
|
||||
|
||||
+103
-98
@@ -1,10 +1,10 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -54,7 +54,7 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||
// 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, pos token.Pos) llvm.Value {
|
||||
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
valueTypes := make([]llvm.Type, len(values))
|
||||
for i, value := range values {
|
||||
valueTypes[i] = value.Type()
|
||||
@@ -128,9 +128,14 @@ func (b *builder) emitPointerPack(values []llvm.Value, pos token.Pos) llvm.Value
|
||||
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
layoutValue := b.createObjectLayout(packedType, pos)
|
||||
align := b.targetData.ABITypeAlignment(packedType)
|
||||
packedAlloc := b.createAlloc(sizeValue, layoutValue, align, "")
|
||||
alloc := b.mod.NamedFunction("runtime.alloc")
|
||||
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
||||
sizeValue,
|
||||
llvm.ConstNull(b.dataPtrType),
|
||||
llvm.Undef(b.dataPtrType), // unused context parameter
|
||||
}, "")
|
||||
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(packedAlloc)
|
||||
}
|
||||
@@ -207,7 +212,7 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
||||
globalType := llvm.ArrayType(elementType, len(buf))
|
||||
global := llvm.AddGlobal(c.mod, globalType, name)
|
||||
value := llvm.Undef(globalType)
|
||||
for i := range buf {
|
||||
for i := 0; i < len(buf); i++ {
|
||||
ch := uint64(buf[i])
|
||||
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
||||
}
|
||||
@@ -226,12 +231,6 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
||||
//
|
||||
// For details on what's in this value, see src/runtime/gc_precise.go.
|
||||
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||
if !typeHasPointers(t) {
|
||||
// There are no pointers in this type, so we can simplify the layout.
|
||||
layout := (uint64(1) << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
|
||||
// Use the element type for arrays. This works even for nested arrays.
|
||||
for {
|
||||
kind := t.TypeKind()
|
||||
@@ -249,29 +248,54 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
break
|
||||
}
|
||||
|
||||
// Create the pointer bitmap.
|
||||
// Do a few checks to see whether we need to generate any object layout
|
||||
// information at all.
|
||||
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||
pointerAlignment := uint64(c.targetData.PrefTypeAlignment(c.dataPtrType))
|
||||
bitmapLen := objectSizeBytes / pointerAlignment
|
||||
bitmapBytes := (bitmapLen + 7) / 8
|
||||
bitmap := make([]byte, bitmapBytes, max(bitmapBytes, 8))
|
||||
c.buildPointerBitmap(bitmap, pointerAlignment, pos, t, 0)
|
||||
|
||||
// Try to encode the layout inline.
|
||||
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
||||
pointerBits := pointerSize * 8
|
||||
if bitmapLen < pointerBits {
|
||||
rawMask := binary.LittleEndian.Uint64(bitmap[0:8])
|
||||
layout := rawMask*pointerBits + bitmapLen
|
||||
layout <<= 1
|
||||
layout |= 1
|
||||
pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||
if objectSizeBytes < pointerSize {
|
||||
// Too small to contain a pointer.
|
||||
layout := (uint64(1) << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
bitmap := c.getPointerBitmap(t, pos)
|
||||
if bitmap.BitLen() == 0 {
|
||||
// There are no pointers in this type, so we can simplify the layout.
|
||||
// TODO: this can be done in many other cases, e.g. when allocating an
|
||||
// array (like [4][]byte, which repeats a slice 4 times).
|
||||
layout := (uint64(1) << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
||||
// This shouldn't happen except for packed structs, which aren't
|
||||
// currently used.
|
||||
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
||||
return llvm.ConstNull(c.dataPtrType)
|
||||
}
|
||||
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
||||
|
||||
// Check if the layout fits.
|
||||
layout &= 1<<pointerBits - 1
|
||||
if (layout>>1)/pointerBits == rawMask {
|
||||
// No set bits were shifted off.
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
pointerBits := pointerSize * 8
|
||||
var sizeFieldBits uint64
|
||||
switch pointerBits {
|
||||
case 16:
|
||||
sizeFieldBits = 4
|
||||
case 32:
|
||||
sizeFieldBits = 5
|
||||
case 64:
|
||||
sizeFieldBits = 6
|
||||
default:
|
||||
panic("unknown pointer size")
|
||||
}
|
||||
layoutFieldBits := pointerBits - 1 - sizeFieldBits
|
||||
|
||||
// Try to emit the value as an inline integer. This is possible in most
|
||||
// cases.
|
||||
if objectSizeWords < layoutFieldBits {
|
||||
// If it can be stored directly in the pointer value, do so.
|
||||
// The runtime knows that if the least significant bit of the pointer is
|
||||
// set, the pointer contains the value itself.
|
||||
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
|
||||
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||
@@ -279,24 +303,25 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
|
||||
// Try first whether the global already exists. All objects with a
|
||||
// particular name have the same type, so this is possible.
|
||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", bitmapLen, (bitmapLen+15)/16, bitmap)
|
||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if !global.IsNil() {
|
||||
return global
|
||||
}
|
||||
|
||||
// Create the global initializer.
|
||||
bitmapByteValues := make([]llvm.Value, bitmapBytes)
|
||||
i8 := c.ctx.Int8Type()
|
||||
for i, b := range bitmap {
|
||||
bitmapByteValues[i] = llvm.ConstInt(i8, uint64(b), false)
|
||||
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))
|
||||
}
|
||||
initializer := c.ctx.ConstStruct([]llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, bitmapLen, false),
|
||||
llvm.ConstArray(i8, bitmapByteValues),
|
||||
llvm.ConstInt(c.uintptrType, objectSizeWords, false),
|
||||
llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues),
|
||||
}, false)
|
||||
|
||||
// Create the actual global.
|
||||
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
||||
global.SetInitializer(initializer)
|
||||
global.SetUnnamedAddr(true)
|
||||
@@ -304,7 +329,6 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
||||
// AVR doesn't have alignment by default.
|
||||
// The lowest bit must be unset to distinguish this from an inline layout.
|
||||
global.SetAlignment(2)
|
||||
}
|
||||
if c.Debug && pos != token.NoPos {
|
||||
@@ -336,71 +360,52 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
return global
|
||||
}
|
||||
|
||||
// buildPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||
// bitmap at the word offset that contains a pointer. This scan is recursive.
|
||||
func (c *compilerContext) buildPointerBitmap(
|
||||
dst []byte,
|
||||
ptrAlign uint64,
|
||||
pos token.Pos,
|
||||
t llvm.Type,
|
||||
offset uint64,
|
||||
) {
|
||||
switch t.TypeKind() {
|
||||
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||
// bigint at the word offset that contains a pointer. This scan is recursive.
|
||||
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
||||
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||
switch typ.TypeKind() {
|
||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
// These types do not contain pointers.
|
||||
|
||||
return big.NewInt(0)
|
||||
case llvm.PointerTypeKind:
|
||||
// Set the corresponding position in the bitmap.
|
||||
dst[offset/8] |= 1 << (offset % 8)
|
||||
|
||||
return big.NewInt(1)
|
||||
case llvm.StructTypeKind:
|
||||
// Recurse over struct elements.
|
||||
for i, et := range t.StructElementTypes() {
|
||||
eo := c.targetData.ElementOffset(t, i)
|
||||
if eo%uint64(ptrAlign) != 0 {
|
||||
if typeHasPointers(et) {
|
||||
// This error will let the compilation fail, but by continuing
|
||||
// the error can still easily be shown.
|
||||
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||
}
|
||||
ptrs := big.NewInt(0)
|
||||
for i, subtyp := range typ.StructElementTypes() {
|
||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||
if subptrs.BitLen() == 0 {
|
||||
continue
|
||||
}
|
||||
c.buildPointerBitmap(
|
||||
dst,
|
||||
ptrAlign,
|
||||
pos,
|
||||
et,
|
||||
offset+(eo/ptrAlign),
|
||||
)
|
||||
}
|
||||
|
||||
case llvm.ArrayTypeKind:
|
||||
// Recurse over array elements.
|
||||
len := t.ArrayLength()
|
||||
if len <= 0 {
|
||||
return
|
||||
}
|
||||
et := t.ElementType()
|
||||
elementSize := c.targetData.TypeAllocSize(et)
|
||||
if elementSize%ptrAlign != 0 {
|
||||
if typeHasPointers(et) {
|
||||
// This error will let the compilation fail (but continues so that
|
||||
// other errors can be shown).
|
||||
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||
offset := c.targetData.ElementOffset(typ, i)
|
||||
if offset%uint64(alignment) != 0 {
|
||||
// This error will let the compilation fail, but by continuing
|
||||
// the error can still easily be shown.
|
||||
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||
continue
|
||||
}
|
||||
return
|
||||
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
}
|
||||
elementSize /= ptrAlign
|
||||
for i := range len {
|
||||
c.buildPointerBitmap(
|
||||
dst,
|
||||
ptrAlign,
|
||||
pos,
|
||||
et,
|
||||
offset+uint64(i)*elementSize,
|
||||
)
|
||||
return ptrs
|
||||
case llvm.ArrayTypeKind:
|
||||
subtyp := typ.ElementType()
|
||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||
ptrs := big.NewInt(0)
|
||||
if subptrs.BitLen() == 0 {
|
||||
return ptrs
|
||||
}
|
||||
|
||||
elementSize := c.targetData.TypeAllocSize(subtyp)
|
||||
if elementSize%uint64(alignment) != 0 {
|
||||
// This error will let the compilation fail (but continues so that
|
||||
// other errors can be shown).
|
||||
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||
return ptrs
|
||||
}
|
||||
for i := 0; i < typ.ArrayLength(); i++ {
|
||||
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
}
|
||||
return ptrs
|
||||
default:
|
||||
// Should not happen.
|
||||
panic("unknown LLVM type")
|
||||
@@ -418,7 +423,7 @@ func (c *compilerContext) archFamily() string {
|
||||
// features string is not one for an ARM architecture.
|
||||
func (c *compilerContext) isThumb() bool {
|
||||
var isThumb, isNotThumb bool
|
||||
for feature := range strings.SplitSeq(c.Features, ",") {
|
||||
for _, feature := range strings.Split(c.Features, ",") {
|
||||
if feature == "+thumb-mode" {
|
||||
isThumb = true
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||
fnType, fn := getLifetimeStartFunc(mod)
|
||||
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, alloca), "")
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,14 +58,14 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
||||
builder.SetInsertPointBefore(inst)
|
||||
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||
fnType, fn := getLifetimeStartFunc(mod)
|
||||
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, alloca), "")
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
||||
builder.SetInsertPointBefore(next)
|
||||
} else {
|
||||
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||
}
|
||||
fnType, fn = getLifetimeEndFunc(mod)
|
||||
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, alloca), "")
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||
return alloca
|
||||
}
|
||||
|
||||
@@ -74,27 +74,7 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
||||
// createTemporaryAlloca.
|
||||
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
|
||||
fnType, fn := getLifetimeEndFunc(mod)
|
||||
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, ptr), "")
|
||||
}
|
||||
|
||||
// lifetimeCallArgs returns the arguments to pass to a call of the
|
||||
// llvm.lifetime.start/end intrinsics. LLVM 22 dropped the (redundant,
|
||||
// already required to match the alloca size) i64 size argument, so the
|
||||
// intrinsic now only takes the pointer.
|
||||
func lifetimeCallArgs(size, ptr llvm.Value) []llvm.Value {
|
||||
if Version() >= 22 {
|
||||
return []llvm.Value{ptr}
|
||||
}
|
||||
return []llvm.Value{size, ptr}
|
||||
}
|
||||
|
||||
// lifetimeFuncType returns the function type of the llvm.lifetime.start/end
|
||||
// intrinsics, which lost their i64 size parameter in LLVM 22.
|
||||
func lifetimeFuncType(ctx llvm.Context, ptrType llvm.Type) llvm.Type {
|
||||
if Version() >= 22 {
|
||||
return llvm.FunctionType(ctx.VoidType(), []llvm.Type{ptrType}, false)
|
||||
}
|
||||
return llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "")
|
||||
}
|
||||
|
||||
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
||||
@@ -104,7 +84,7 @@ func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||
fn := mod.NamedFunction(fnName)
|
||||
ctx := mod.Context()
|
||||
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
fnType := lifetimeFuncType(ctx, ptrType)
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||
}
|
||||
@@ -118,7 +98,7 @@ func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||
fn := mod.NamedFunction(fnName)
|
||||
ctx := mod.Context()
|
||||
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
fnType := lifetimeFuncType(ctx, ptrType)
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||
}
|
||||
@@ -238,36 +218,6 @@ func Version() int {
|
||||
return major
|
||||
}
|
||||
|
||||
// NoCaptureAttrName returns the name of the LLVM attribute kind that marks a
|
||||
// pointer parameter as guaranteed not to escape by capture.
|
||||
//
|
||||
// LLVM 21 removed the old boolean 'nocapture' enum attribute in favor of the
|
||||
// more expressive 'captures' int attribute, where 'captures(none)' (encoded
|
||||
// as the value 0) is the equivalent of the old 'nocapture'. LLVM 20 supports
|
||||
// both, but its own optimizer still emits 'nocapture', so the cutoff for
|
||||
// reading/writing the new name is LLVM 21.
|
||||
func NoCaptureAttrName() string {
|
||||
if Version() >= 21 {
|
||||
return "captures"
|
||||
}
|
||||
return "nocapture"
|
||||
}
|
||||
|
||||
// IsNoCapture reports whether attr (looked up using the kind returned by
|
||||
// NoCaptureAttrName) indicates that the pointer it is attached to does not
|
||||
// escape by capture. It returns false for a nil attribute.
|
||||
func IsNoCapture(attr llvm.Attribute) bool {
|
||||
if attr.IsNil() {
|
||||
return false
|
||||
}
|
||||
if Version() >= 21 {
|
||||
// captures(none) is encoded as the value 0; any other value permits
|
||||
// some form of capture.
|
||||
return attr.GetEnumValue() == 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ByteOrder returns the byte order for the given target triple. Most targets are little
|
||||
// endian, but for example MIPS can be big-endian.
|
||||
func ByteOrder(target string) binary.ByteOrder {
|
||||
|
||||
+184
-460
@@ -3,29 +3,42 @@ package compiler
|
||||
// This file emits the correct map intrinsics for map operations.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/tinygo-org/tinygo/src/tinygo"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"strings"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
const hashArrayUnrollLimit = 4
|
||||
|
||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||
// initializing an appropriately sized object.
|
||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
mapType := expr.Type().Underlying().(*types.Map)
|
||||
keyType := mapType.Key().Underlying()
|
||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
|
||||
var llvmKeyType llvm.Type
|
||||
var alg uint64
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// String keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = uint64(tinygo.HashmapAlgorithmString)
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// Trivially comparable keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = uint64(tinygo.HashmapAlgorithmBinary)
|
||||
} else {
|
||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||
// implementation.
|
||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
||||
alg = uint64(tinygo.HashmapAlgorithmInterface)
|
||||
}
|
||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
|
||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
||||
if expr.Reserve != nil {
|
||||
sizeHint = b.getValue(expr.Reserve, getPos(expr))
|
||||
var err error
|
||||
@@ -34,130 +47,135 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve hash and equal functions for this key type. For string and
|
||||
// binary key types, reference the corresponding runtime functions
|
||||
// directly. For composite types, generate type-specific functions.
|
||||
var hashFn, equalFn llvm.Value
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
hashFn = b.getRuntimeFunctionValue("hashmapStringPtrHash", hashmapKeyHashSignature())
|
||||
equalFn = b.getRuntimeFunctionValue("hashmapStringEqual", hashmapKeyEqualSignature())
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
hashFn = b.getRuntimeFunctionValue("hash32", hashmapKeyHashSignature())
|
||||
equalFn = b.getRuntimeFunctionValue("memequal", hashmapKeyEqualSignature())
|
||||
} else {
|
||||
fn := b.getOrGenerateKeyHashFunc(keyType)
|
||||
hashFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyHashSignature())
|
||||
fn = b.getOrGenerateKeyEqualFunc(keyType)
|
||||
equalFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyEqualSignature())
|
||||
}
|
||||
|
||||
hashmap := b.createRuntimeCall("hashmapMakeGeneric", []llvm.Value{
|
||||
llvmKeySize, llvmValueSize, sizeHint,
|
||||
hashFn, equalFn,
|
||||
}, "")
|
||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
||||
return hashmap, nil
|
||||
}
|
||||
|
||||
// getRuntimeFunctionValue returns a TinyGo function value (with nil context)
|
||||
// for the named runtime function.
|
||||
func (b *builder) getRuntimeFunctionValue(name string, sig *types.Signature) llvm.Value {
|
||||
member := b.program.ImportedPackage("runtime").Members[name]
|
||||
if member == nil {
|
||||
panic("unknown runtime function: " + name)
|
||||
}
|
||||
_, llvmFn := b.getFunction(member.(*ssa.Function))
|
||||
return b.createFuncValue(llvmFn, llvm.ConstNull(b.dataPtrType), sig)
|
||||
}
|
||||
|
||||
// createMapLookup returns the value in a map. It calls a runtime function
|
||||
// depending on the map key type to load the map value and its comma-ok value.
|
||||
func (b *builder) createMapLookup(keyType, valueType types.Type, m llvm.Value, key ssa.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||
func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||
llvmValueType := b.getLLVMType(valueType)
|
||||
|
||||
// Allocate the memory for the resulting type. Do not zero this memory: it
|
||||
// will be zeroed by the hashmap get implementation if the key is not
|
||||
// present in the map.
|
||||
result := b.createRuntimeValueResult(llvmValueType, commaOk, false, "hashmap")
|
||||
mapValueAlloca := result.valuePtr
|
||||
mapValueAlloca, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
||||
|
||||
// We need the map size (with type uintptr) to pass to the hashmap*Get
|
||||
// functions. This is necessary because those *Get functions are valid on
|
||||
// nil maps, and they'll need to zero the value pointer by that number of
|
||||
// bytes.
|
||||
mapValueSize := result.valueSize
|
||||
mapValueSize := mapValueAllocaSize
|
||||
if mapValueSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
||||
mapValueSize = llvm.ConstTrunc(mapValueSize, b.uintptrType)
|
||||
}
|
||||
|
||||
// Do the lookup. How it is done depends on the key type.
|
||||
var commaOkValue llvm.Value
|
||||
origKeyType := keyType
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, b.getValue(key, getPos(key)), mapValueAlloca, mapValueSize}
|
||||
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
|
||||
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
||||
// growth.
|
||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
b.CreateStore(key, mapKeyAlloca)
|
||||
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
|
||||
// Fetch the value from the hashmap.
|
||||
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
|
||||
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||
} else {
|
||||
// Key stored at actual type: either binary-comparable or with
|
||||
// compiler-generated hash/equal.
|
||||
mapKey := b.getValueStorage(key, "hashmap.key")
|
||||
params := []llvm.Value{m, mapKey.ptr, mapValueAlloca, mapValueSize}
|
||||
fnName := "hashmapBinaryGet"
|
||||
if !hashmapIsBinaryKey(keyType) {
|
||||
fnName = "hashmapGenericGet"
|
||||
// Not trivially comparable using memcmp. Make it an interface instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface now.
|
||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||
}
|
||||
commaOkValue = b.createRuntimeCall(fnName, params, "")
|
||||
b.endValueStorage(mapKey)
|
||||
params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
|
||||
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
|
||||
}
|
||||
|
||||
// The value is set to the zero value if the key doesn't exist.
|
||||
return result.finish(b, commaOkValue, ""), nil
|
||||
// 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, "")
|
||||
b.emitLifetimeEnd(mapValueAlloca, mapValueAllocaSize)
|
||||
|
||||
if commaOk {
|
||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false))
|
||||
tuple = b.CreateInsertValue(tuple, mapValue, 0, "")
|
||||
tuple = b.CreateInsertValue(tuple, commaOkValue, 1, "")
|
||||
return tuple, nil
|
||||
} else {
|
||||
return mapValue, nil
|
||||
}
|
||||
}
|
||||
|
||||
// createMapUpdate updates a map key to a given value, by creating an
|
||||
// appropriate runtime call.
|
||||
func (b *builder) createMapUpdate(keyType types.Type, m llvm.Value, key, value ssa.Value, pos token.Pos) {
|
||||
storedValue := b.getValueStorage(value, "hashmap.value")
|
||||
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
||||
b.CreateStore(value, valueAlloca)
|
||||
origKeyType := keyType
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, b.getValue(key, getPos(key)), storedValue.ptr}
|
||||
b.createRuntimeInvoke("hashmapStringSet", params, "")
|
||||
params := []llvm.Value{m, key, valueAlloca}
|
||||
b.createRuntimeCall("hashmapStringSet", params, "")
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
b.CreateStore(key, keyAlloca)
|
||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
||||
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
||||
b.createRuntimeCall("hashmapBinarySet", params, "")
|
||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||
} else {
|
||||
// Key stored at actual type.
|
||||
keyStorage := b.getValueStorage(key, "hashmap.key")
|
||||
fnName := "hashmapBinarySet"
|
||||
if !hashmapIsBinaryKey(keyType) {
|
||||
fnName = "hashmapGenericSet"
|
||||
// Key is not trivially comparable, so compare it as an interface instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface first.
|
||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||
}
|
||||
params := []llvm.Value{m, keyStorage.ptr, storedValue.ptr}
|
||||
b.createRuntimeInvoke(fnName, params, "")
|
||||
b.endValueStorage(keyStorage)
|
||||
params := []llvm.Value{m, itfKey, valueAlloca}
|
||||
b.createRuntimeCall("hashmapInterfaceSet", params, "")
|
||||
}
|
||||
b.endValueStorage(storedValue)
|
||||
b.emitLifetimeEnd(valueAlloca, valueSize)
|
||||
}
|
||||
|
||||
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
||||
// function. It is the implementation of the Go delete() builtin.
|
||||
func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||
origKeyType := keyType
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, key}
|
||||
b.createRuntimeCall("hashmapStringDelete", params, "")
|
||||
return nil
|
||||
} else {
|
||||
// Key stored at actual type.
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
b.CreateStore(key, keyAlloca)
|
||||
fnName := "hashmapBinaryDelete"
|
||||
if !hashmapIsBinaryKey(keyType) {
|
||||
fnName = "hashmapGenericDelete"
|
||||
}
|
||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
||||
params := []llvm.Value{m, keyAlloca}
|
||||
b.createRuntimeCall(fnName, params, "")
|
||||
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||
return nil
|
||||
} else {
|
||||
// Key is not trivially comparable, so compare it as an interface
|
||||
// instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface first.
|
||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||
}
|
||||
params := []llvm.Value{m, itfKey}
|
||||
b.createRuntimeCall("hashmapInterfaceDelete", params, "")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,15 +195,42 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
llvmValueType := b.getLLVMType(valueType)
|
||||
|
||||
// All key types are now stored at their declared type (no interface wrapping).
|
||||
// There is a special case in which keys are stored as an interface value
|
||||
// instead of the value they normally are. This happens for non-trivially
|
||||
// comparable types such as float32 or some structs.
|
||||
isKeyStoredAsInterface := false
|
||||
if t, ok := keyType.Underlying().(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
} else {
|
||||
// The key is stored as an interface value, and may or may not be an
|
||||
// interface type (for example, float32 keys are stored as an interface
|
||||
// value).
|
||||
if _, ok := keyType.Underlying().(*types.Interface); !ok {
|
||||
isKeyStoredAsInterface = true
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the type of the key as stored in the map.
|
||||
llvmStoredKeyType := llvmKeyType
|
||||
if isKeyStoredAsInterface {
|
||||
llvmStoredKeyType = b.getLLVMRuntimeType("_interface")
|
||||
}
|
||||
|
||||
// Extract the key and value from the map.
|
||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmKeyType, "range.key")
|
||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
||||
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
||||
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
|
||||
mapKey := b.CreateLoad(llvmKeyType, mapKeyAlloca, "")
|
||||
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||
|
||||
if isKeyStoredAsInterface {
|
||||
// The key is stored as an interface but it isn't of interface type.
|
||||
// Extract the underlying value.
|
||||
mapKey = b.extractValueFromInterface(mapKey, llvmKeyType)
|
||||
}
|
||||
|
||||
// End the lifetimes of the allocas, because we're done with them.
|
||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
|
||||
@@ -205,9 +250,20 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 || keyType.Kind() == types.UnsafePointer
|
||||
// TODO: unsafe.Pointer is also a binary key, but to support that we
|
||||
// need to fix an issue with interp first (see
|
||||
// https://github.com/tinygo-org/tinygo/pull/4898).
|
||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
||||
case *types.Pointer:
|
||||
return true
|
||||
case *types.Struct:
|
||||
for i := 0; i < keyType.NumFields(); i++ {
|
||||
fieldType := keyType.Field(i).Type().Underlying()
|
||||
if !hashmapIsBinaryKey(fieldType) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Array:
|
||||
return hashmapIsBinaryKey(keyType.Elem())
|
||||
default:
|
||||
@@ -215,400 +271,68 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// hashmapKeyHashSignature returns the Go type signature for hashmap key hash
|
||||
// functions: func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||
func hashmapKeyHashSignature() *types.Signature {
|
||||
return types.NewSignatureType(nil, nil, nil,
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "key", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uintptr]),
|
||||
types.NewVar(token.NoPos, nil, "seed", types.Typ[types.Uintptr]),
|
||||
),
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "", types.Typ[types.Uint32]),
|
||||
),
|
||||
false,
|
||||
)
|
||||
}
|
||||
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.
|
||||
|
||||
// hashmapKeyEqualSignature returns the Go type signature for hashmap key equal
|
||||
// functions: func(x, y unsafe.Pointer, n uintptr) bool
|
||||
func hashmapKeyEqualSignature() *types.Signature {
|
||||
return types.NewSignatureType(nil, nil, nil,
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "x", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "y", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "n", types.Typ[types.Uintptr]),
|
||||
),
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "", types.Typ[types.Bool]),
|
||||
),
|
||||
false,
|
||||
)
|
||||
}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
|
||||
// hashmapKeyFuncName returns a canonical name for a generated hash or equal
|
||||
// function based on the key type's underlying structure. Named types are
|
||||
// replaced with their underlying types so that structurally identical key
|
||||
// types (e.g., struct{i1; str1} and struct{i2; str2} where both i1, i2 are
|
||||
// int and str1, str2 are string) share the same generated function.
|
||||
func hashmapKeyFuncName(prefix string, keyType types.Type) string {
|
||||
return prefix + "." + hashmapCanonicalTypeName(keyType)
|
||||
}
|
||||
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()
|
||||
|
||||
// hashmapCanonicalTypeName returns a string representation of the hash/equal
|
||||
// operations needed for a type, stripping named types where the operation does
|
||||
// not depend on the name. Pointer and channel names do not include the element
|
||||
// type because their hash/equal operations only use the pointer word.
|
||||
func hashmapCanonicalTypeName(t types.Type) string {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return t.Name()
|
||||
case *types.Pointer:
|
||||
return "*"
|
||||
case *types.Chan:
|
||||
switch t.Dir() {
|
||||
case types.SendRecv:
|
||||
return "chan"
|
||||
case types.SendOnly:
|
||||
return "chan<-"
|
||||
case types.RecvOnly:
|
||||
return "<-chan"
|
||||
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 *types.Interface:
|
||||
if t.NumMethods() == 0 {
|
||||
return "interface{}"
|
||||
}
|
||||
return t.String()
|
||||
case *types.Struct:
|
||||
var s strings.Builder
|
||||
s.WriteString("struct{")
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
if i > 0 {
|
||||
s.WriteString("; ")
|
||||
}
|
||||
s.WriteString(hashmapCanonicalTypeName(t.Field(i).Type()))
|
||||
}
|
||||
return s.String() + "}"
|
||||
case *types.Array:
|
||||
return fmt.Sprintf("[%d]%s", t.Len(), hashmapCanonicalTypeName(t.Elem()))
|
||||
}
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// getOrGenerateKeyHashFunc returns an LLVM function that computes the hash
|
||||
// of a key of the given type. The function is generated on first call and
|
||||
// cached in the module.
|
||||
func (b *builder) getOrGenerateKeyHashFunc(keyType types.Type) llvm.Value {
|
||||
name := hashmapKeyFuncName("hashmapKeyHash", keyType)
|
||||
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||
return fn
|
||||
}
|
||||
case llvm.StructTypeKind:
|
||||
llvmStructType := llvmType
|
||||
numFields := llvmStructType.StructElementTypesCount()
|
||||
llvmElementTypes := llvmStructType.StructElementTypes()
|
||||
|
||||
// Create the LLVM function type:
|
||||
// (key ptr, size uintptr, seed uintptr, context ptr) -> i32
|
||||
fnType := llvm.FunctionType(b.ctx.Int32Type(), []llvm.Type{
|
||||
b.dataPtrType, b.uintptrType, b.uintptrType, b.dataPtrType,
|
||||
}, false)
|
||||
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
fn.SetUnnamedAddr(true)
|
||||
b.addStandardAttributes(fn)
|
||||
|
||||
// Generate the function body.
|
||||
savedBlock := b.GetInsertBlock()
|
||||
defer b.SetInsertPointAtEnd(savedBlock)
|
||||
|
||||
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
keyPtr := fn.Param(0)
|
||||
seed := fn.Param(2)
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
hash := b.generateKeyHash(keyType, llvmKeyType, keyPtr, seed)
|
||||
b.CreateRet(hash)
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
// getOrGenerateKeyEqualFunc returns an LLVM function that compares two keys
|
||||
// of the given type for equality. The function is generated on first call
|
||||
// and cached in the module.
|
||||
func (b *builder) getOrGenerateKeyEqualFunc(keyType types.Type) llvm.Value {
|
||||
name := hashmapKeyFuncName("hashmapKeyEqual", keyType)
|
||||
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||
return fn
|
||||
}
|
||||
|
||||
// Create the LLVM function type:
|
||||
// (x ptr, y ptr, n uintptr, context ptr) -> i1
|
||||
fnType := llvm.FunctionType(b.ctx.Int1Type(), []llvm.Type{
|
||||
b.dataPtrType, b.dataPtrType, b.uintptrType, b.dataPtrType,
|
||||
}, false)
|
||||
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
fn.SetUnnamedAddr(true)
|
||||
b.addStandardAttributes(fn)
|
||||
|
||||
// Generate the function body.
|
||||
savedBlock := b.GetInsertBlock()
|
||||
defer b.SetInsertPointAtEnd(savedBlock)
|
||||
|
||||
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
xPtr := fn.Param(0)
|
||||
yPtr := fn.Param(1)
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
result := b.generateKeyEqual(keyType, llvmKeyType, xPtr, yPtr, fn)
|
||||
b.CreateRet(result)
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
// generateKeyHash generates IR that hashes a key value. Returns the i32 hash.
|
||||
func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, keyPtr llvm.Value, seed llvm.Value) llvm.Value {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
// Hash the string contents. The size parameter is unused by
|
||||
// hashmapStringPtrHash (it dereferences the string header to
|
||||
// get the actual length), but we pass it for signature
|
||||
// consistency with other hash functions.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hashmapStringPtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
}
|
||||
if keyType.Info()&types.IsFloat != 0 {
|
||||
// Float hash: normalizes -0 to +0 before hashing.
|
||||
if keyType.Kind() == types.Float32 {
|
||||
return b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||
}
|
||||
return b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||
}
|
||||
if keyType.Info()&types.IsComplex != 0 {
|
||||
// Complex hash: hash real and imaginary parts as floats.
|
||||
if keyType.Kind() == types.Complex64 {
|
||||
realPtr := keyPtr
|
||||
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||
llvm.ConstInt(b.uintptrType, 4, false),
|
||||
}, "")
|
||||
realHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||
imagHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||
return b.CreateXor(realHash, imagHash, "")
|
||||
}
|
||||
realPtr := keyPtr
|
||||
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||
llvm.ConstInt(b.uintptrType, 8, false),
|
||||
}, "")
|
||||
realHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||
imagHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||
return b.CreateXor(realHash, imagHash, "")
|
||||
}
|
||||
// Integer/boolean: hash the raw bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
case *types.Pointer, *types.Chan:
|
||||
// Pointers and channels: hash as raw pointer-sized bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
case *types.Interface:
|
||||
// Interface: use runtime reflection-based hash.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hashmapInterfacePtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
case *types.Struct:
|
||||
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < keyType.NumFields(); i++ {
|
||||
if keyType.Field(i).Name() == "_" {
|
||||
continue // blank fields are ignored in Go equality
|
||||
}
|
||||
fieldType := keyType.Field(i).Type()
|
||||
llvmFieldType := b.getLLVMType(fieldType)
|
||||
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||
continue // skip zero-sized fields
|
||||
}
|
||||
for i := 0; i < numFields; i++ {
|
||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||
fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||
fieldHash := b.generateKeyHash(fieldType, llvmFieldType, fieldPtr, seed)
|
||||
hash = b.CreateXor(hash, fieldHash, "")
|
||||
}
|
||||
return hash
|
||||
case *types.Array:
|
||||
elemType := keyType.Elem()
|
||||
llvmElemType := b.getLLVMType(elemType)
|
||||
arrayLen := keyType.Len()
|
||||
if hashmapIsBinaryKey(elemType) {
|
||||
// All elements are binary-comparable; hash the entire array as raw bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||
}
|
||||
if arrayLen == 0 {
|
||||
return llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
}
|
||||
if arrayLen <= hashArrayUnrollLimit {
|
||||
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < int(arrayLen); i++ {
|
||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||
hash = b.CreateXor(hash, elemHash, "")
|
||||
}
|
||||
return hash
|
||||
}
|
||||
initHash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
|
||||
|
||||
loopEntry := b.GetInsertBlock()
|
||||
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.body")
|
||||
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.done")
|
||||
// zero any padding bytes in this field
|
||||
llvmElemType := llvmElementTypes[i]
|
||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
||||
|
||||
b.CreateBr(loopBody)
|
||||
b.SetInsertPointAtEnd(loopBody)
|
||||
// zero any padding bytes before the next field, if any
|
||||
offset := b.targetData.ElementOffset(llvmStructType, i)
|
||||
storeSize := b.targetData.TypeStoreSize(llvmElemType)
|
||||
fieldEndOffset := offset + storeSize
|
||||
|
||||
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||
phiHash := b.CreatePHI(b.ctx.Int32Type(), "hash.acc")
|
||||
|
||||
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, phiI}, "")
|
||||
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||
newHash := b.CreateXor(phiHash, elemHash, "")
|
||||
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||
cond := b.CreateICmp(llvm.IntULT, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||
b.CreateCondBr(cond, loopBody, loopDone)
|
||||
|
||||
bodyEnd := b.GetInsertBlock()
|
||||
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||
phiHash.AddIncoming([]llvm.Value{initHash, newHash},
|
||||
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||
|
||||
b.SetInsertPointAtEnd(loopDone)
|
||||
return newHash
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled key type for hash generation: %T", keyType))
|
||||
}
|
||||
}
|
||||
|
||||
// generateKeyEqual generates IR that compares two key values for equality.
|
||||
// Returns an i1 result.
|
||||
func (b *builder) generateKeyEqual(keyType types.Type, llvmKeyType llvm.Type, xPtr, yPtr llvm.Value, fn llvm.Value) llvm.Value {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
// Compare strings: load both string headers and compare.
|
||||
xStr := b.CreateLoad(llvmKeyType, xPtr, "x.str")
|
||||
yStr := b.CreateLoad(llvmKeyType, yPtr, "y.str")
|
||||
return b.createRuntimeCall("stringEqual", []llvm.Value{xStr, yStr}, "eq")
|
||||
}
|
||||
if keyType.Info()&types.IsFloat != 0 {
|
||||
// Float equality: fcmp oeq handles -0==+0 (true) and NaN==NaN (false).
|
||||
xVal := b.CreateLoad(llvmKeyType, xPtr, "x.float")
|
||||
yVal := b.CreateLoad(llvmKeyType, yPtr, "y.float")
|
||||
return b.CreateFCmp(llvm.FloatOEQ, xVal, yVal, "eq")
|
||||
}
|
||||
if keyType.Info()&types.IsComplex != 0 {
|
||||
// Complex equality: both real and imaginary parts must be equal.
|
||||
var floatType llvm.Type
|
||||
if keyType.Kind() == types.Complex64 {
|
||||
floatType = b.ctx.FloatType()
|
||||
var nextOffset uint64
|
||||
if i < numFields-1 {
|
||||
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
|
||||
} else {
|
||||
floatType = b.ctx.DoubleType()
|
||||
// Last field? Next offset is the total size of the allocate struct.
|
||||
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
|
||||
}
|
||||
floatSize := b.targetData.TypeAllocSize(floatType)
|
||||
imagOffset := llvm.ConstInt(b.uintptrType, floatSize, false)
|
||||
// Real parts
|
||||
xReal := b.CreateLoad(floatType, xPtr, "x.real")
|
||||
yReal := b.CreateLoad(floatType, yPtr, "y.real")
|
||||
realEq := b.CreateFCmp(llvm.FloatOEQ, xReal, yReal, "eq.real")
|
||||
// Imaginary parts
|
||||
xImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), xPtr, []llvm.Value{imagOffset}, "")
|
||||
yImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), yPtr, []llvm.Value{imagOffset}, "")
|
||||
xImag := b.CreateLoad(floatType, xImagPtr, "x.imag")
|
||||
yImag := b.CreateLoad(floatType, yImagPtr, "y.imag")
|
||||
imagEq := b.CreateFCmp(llvm.FloatOEQ, xImag, yImag, "eq.imag")
|
||||
return b.CreateAnd(realEq, imagEq, "")
|
||||
}
|
||||
// Integer/boolean: compare raw bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
case *types.Pointer, *types.Chan:
|
||||
// Pointers and channels: compare as raw pointer-sized bytes.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
case *types.Interface:
|
||||
// Interface: use runtime interface equality.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("hashmapInterfaceEqual", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
case *types.Struct:
|
||||
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false) // start with true
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < keyType.NumFields(); i++ {
|
||||
if keyType.Field(i).Name() == "_" {
|
||||
continue // blank fields are ignored in Go equality
|
||||
|
||||
if fieldEndOffset != nextOffset {
|
||||
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
|
||||
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
|
||||
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
|
||||
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
|
||||
}
|
||||
fieldType := keyType.Field(i).Type()
|
||||
llvmFieldType := b.getLLVMType(fieldType)
|
||||
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||
continue // skip zero-sized fields
|
||||
}
|
||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||
xFieldPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||
yFieldPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||
fieldEq := b.generateKeyEqual(fieldType, llvmFieldType, xFieldPtr, yFieldPtr, fn)
|
||||
result = b.CreateAnd(result, fieldEq, "")
|
||||
}
|
||||
return result
|
||||
case *types.Array:
|
||||
elemType := keyType.Elem()
|
||||
llvmElemType := b.getLLVMType(elemType)
|
||||
arrayLen := keyType.Len()
|
||||
if hashmapIsBinaryKey(elemType) {
|
||||
// All elements are binary-comparable; compare the entire array.
|
||||
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||
}
|
||||
if arrayLen == 0 {
|
||||
return llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||
}
|
||||
if arrayLen <= hashArrayUnrollLimit {
|
||||
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < int(arrayLen); i++ {
|
||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||
result = b.CreateAnd(result, elemEq, "")
|
||||
}
|
||||
return result
|
||||
}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
|
||||
loopEntry := b.GetInsertBlock()
|
||||
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.body")
|
||||
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.done")
|
||||
|
||||
b.CreateBr(loopBody)
|
||||
b.SetInsertPointAtEnd(loopBody)
|
||||
|
||||
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||
|
||||
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, phiI}, "")
|
||||
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, phiI}, "")
|
||||
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||
|
||||
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||
atEnd := b.CreateICmp(llvm.IntUGE, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||
exitLoop := b.CreateOr(atEnd, b.CreateNot(elemEq, ""), "")
|
||||
b.CreateCondBr(exitLoop, loopDone, loopBody)
|
||||
|
||||
bodyEnd := b.GetInsertBlock()
|
||||
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||
|
||||
b.SetInsertPointAtEnd(loopDone)
|
||||
return elemEq
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled key type for equal generation: %T", keyType))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+2
-1
@@ -29,7 +29,8 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||
// field f of x, but at least 1."
|
||||
max := int64(1)
|
||||
for f := range t.Fields() {
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
f := t.Field(i)
|
||||
if a := s.Alignof(f.Type()); a > max {
|
||||
max = a
|
||||
}
|
||||
|
||||
+48
-161
@@ -8,7 +8,6 @@ import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -35,7 +34,6 @@ type functionInfo struct {
|
||||
interrupt bool // go:interrupt
|
||||
nobounds bool // go:nobounds
|
||||
noescape bool // go:noescape
|
||||
noheap bool // go:noheap
|
||||
variadic bool // go:variadic (CGo only)
|
||||
inline inlineType // go:inline
|
||||
}
|
||||
@@ -79,27 +77,24 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
return llvmFn.GlobalValueType(), llvmFn
|
||||
}
|
||||
|
||||
retType, indirectResult := c.hasIndirectResult(fn.Signature)
|
||||
if info.exported {
|
||||
indirectResult = false
|
||||
var retType llvm.Type
|
||||
if fn.Signature.Results() == nil {
|
||||
retType = c.ctx.VoidType()
|
||||
} else if fn.Signature.Results().Len() == 1 {
|
||||
retType = c.getLLVMType(fn.Signature.Results().At(0).Type())
|
||||
} else {
|
||||
results := make([]llvm.Type, 0, fn.Signature.Results().Len())
|
||||
for i := 0; i < fn.Signature.Results().Len(); i++ {
|
||||
results = append(results, c.getLLVMType(fn.Signature.Results().At(i).Type()))
|
||||
}
|
||||
retType = c.ctx.StructType(results, false)
|
||||
}
|
||||
|
||||
var paramInfos []paramInfo
|
||||
if indirectResult {
|
||||
paramInfos = append(paramInfos, paramInfo{
|
||||
llvmType: c.dataPtrType,
|
||||
name: "return",
|
||||
elemSize: c.targetData.TypeAllocSize(retType),
|
||||
})
|
||||
retType = c.ctx.VoidType()
|
||||
}
|
||||
for _, param := range getParams(fn.Signature) {
|
||||
paramType := c.getLLVMType(param.Type())
|
||||
if info.exported {
|
||||
paramInfos = append(paramInfos, c.expandDirectFormalParamType(paramType, param.Name(), param.Type())...)
|
||||
} else {
|
||||
paramInfos = append(paramInfos, c.expandFormalParamType(paramType, param.Name(), param.Type())...)
|
||||
}
|
||||
paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type())
|
||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||
}
|
||||
|
||||
// Add an extra parameter as the function context. This context is used in
|
||||
@@ -109,20 +104,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
}
|
||||
|
||||
var paramTypes []llvm.Type
|
||||
hasIndirectABI := indirectResult
|
||||
for _, info := range paramInfos {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
hasIndirectABI = hasIndirectABI || info.flags¶mIsIndirect != 0
|
||||
}
|
||||
|
||||
fnType := llvm.FunctionType(retType, paramTypes, info.variadic)
|
||||
llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType)
|
||||
if hasIndirectABI {
|
||||
// Argument promotion only rewrites functions whose uses are all direct
|
||||
// calls. Keep an address use so LLVM cannot reconstruct the large
|
||||
// aggregate signature that this ABI exists to avoid.
|
||||
llvmutil.AppendToGlobal(c.mod, "llvm.used", llvmFn)
|
||||
}
|
||||
if strings.HasPrefix(c.Triple, "wasm") {
|
||||
// C functions without prototypes like this:
|
||||
// void foo();
|
||||
@@ -152,7 +139,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
// not.
|
||||
// (It may be safe to add the nocapture parameter to the context
|
||||
// parameter, but I'd like to stay on the safe side here).
|
||||
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0)
|
||||
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||
}
|
||||
if paramInfo.flags¶mIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||
@@ -171,10 +158,10 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
// Mark it as noreturn so LLVM can optimize away code.
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
||||
case "internal/abi.NoEscape":
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
case "runtime.alloc", "runtime.alloc_noheap", "runtime.alloc_zero":
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.alloc":
|
||||
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
||||
// returns values that are never null and never alias to an existing value.
|
||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||
@@ -195,44 +182,19 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
case "runtime.sliceAppend":
|
||||
// Appending a slice will only read the to-be-appended slice, it won't
|
||||
// be modified.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.stringFromBytes":
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.stringFromRunes":
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.hashmapSet":
|
||||
// The key (param 2) and value (param 3) pointers are only read via
|
||||
// memcpy/hash/equal and are never captured. The indirect calls
|
||||
// through m.keyHash and m.keyEqual function pointers prevent LLVM's
|
||||
// functionattrs pass from inferring this automatically.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
case "runtime.hashmapGet":
|
||||
// The key (param 2) is read-only and never captured.
|
||||
// The value (param 3) is written to (receives the result) but never captured.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
case "runtime.hashmapDelete":
|
||||
// The key (param 2) is read-only and never captured.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
case "runtime.hashmapGenericSet":
|
||||
// Same as hashmapBinarySet: key (param 2) and value (param 3) are
|
||||
// not captured.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
case "runtime.hashmapGenericGet":
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
case "runtime.hashmapGenericDelete":
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
case "runtime.trackPointer":
|
||||
// This function is necessary for tracking pointers on the stack in a
|
||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||
// that the only thing we'll do is read the pointer.
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||
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" {
|
||||
@@ -267,7 +229,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
|
||||
}
|
||||
nocaptureKind := llvm.AttributeKindID(llvmutil.NoCaptureAttrName())
|
||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||
for i, typ := range paramTypes {
|
||||
if typ.TypeKind() == llvm.PointerTypeKind {
|
||||
@@ -331,7 +293,7 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
||||
}
|
||||
info := functionInfo{
|
||||
// Pick the default linkName.
|
||||
linkName: c.canonicalFunctionName(f),
|
||||
linkName: f.RelString(nil),
|
||||
}
|
||||
|
||||
// Check for a few runtime functions that are treated specially.
|
||||
@@ -358,18 +320,6 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
func (c *compilerContext) canonicalFunctionName(f *ssa.Function) string {
|
||||
typeArgs := f.TypeArgs()
|
||||
if len(typeArgs) == 0 {
|
||||
return f.RelString(nil)
|
||||
}
|
||||
parts := make([]string, len(typeArgs))
|
||||
for i, ta := range typeArgs {
|
||||
parts[i], _ = c.getTypeCodeName(ta)
|
||||
}
|
||||
return f.Origin().RelString(nil) + "[" + strings.Join(parts, ",") + "]"
|
||||
}
|
||||
|
||||
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
||||
// //export or //go:noinline.
|
||||
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
@@ -396,51 +346,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
}
|
||||
}
|
||||
|
||||
// Also scan file-level //go:linkname directives. These appear as
|
||||
// free-standing comments in *ast.File.Comments (not attached to any
|
||||
// declaration), and are used by modern golang.org/x/sys/unix and others.
|
||||
// Function-attached directives (above) take precedence — we only add
|
||||
// file-level ones if no doc-comment linkname was found for this function.
|
||||
//
|
||||
// TODO: the hasUnsafeImport gate enforced downstream (see the
|
||||
// //go:linkname case below) is package-level. gc enforces it per
|
||||
// file, on the file containing the directive. For file-level
|
||||
// linknames this is more important than for function-attached ones,
|
||||
// because the directive can live in a file separate from the
|
||||
// function. A stricter implementation would check whether the file
|
||||
// returned by fileForFunc imports "unsafe", not whether any file in
|
||||
// the package does.
|
||||
hasFunctionLinkname := false
|
||||
for _, comment := range pragmas {
|
||||
if strings.HasPrefix(comment.Text, "//go:linkname ") {
|
||||
parts := strings.Fields(comment.Text)
|
||||
if len(parts) == 3 && parts[1] == f.Name() {
|
||||
hasFunctionLinkname = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasFunctionLinkname {
|
||||
if file := c.fileForFunc(f); file != nil {
|
||||
for _, group := range file.Comments {
|
||||
// Skip the function's own doc comment — already handled above.
|
||||
if decl, ok := syntax.(*ast.FuncDecl); ok && group == decl.Doc {
|
||||
continue
|
||||
}
|
||||
for _, comment := range group.List {
|
||||
if !strings.HasPrefix(comment.Text, "//go:linkname ") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(comment.Text)
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
}
|
||||
pragmas = append(pragmas, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse each pragma.
|
||||
for _, comment := range pragmas {
|
||||
parts := strings.Fields(comment.Text)
|
||||
@@ -458,7 +363,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
info.wasmName = info.linkName
|
||||
info.exported = true
|
||||
case "//go:interrupt":
|
||||
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.interrupt = true
|
||||
}
|
||||
case "//go:wasm-module":
|
||||
@@ -512,7 +417,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
info.inline = inlineHint
|
||||
case "//go:noinline":
|
||||
info.inline = inlineNone
|
||||
case "//go:linkname", "//go:linknamestd":
|
||||
case "//go:linkname":
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
}
|
||||
@@ -520,14 +425,14 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
// This is a slightly looser requirement than what gc uses: gc
|
||||
// requires the file to import "unsafe", not the package as a
|
||||
// whole.
|
||||
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.linkName = parts[2]
|
||||
}
|
||||
case "//go:section":
|
||||
// Only enable go:section when the package imports "unsafe".
|
||||
// go:section also implies go:noinline since inlining could
|
||||
// move the code to a different section than that requested.
|
||||
if len(parts) == 2 && slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.section = parts[1]
|
||||
info.inline = inlineNone
|
||||
}
|
||||
@@ -536,7 +441,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
// runtime functions.
|
||||
// This is somewhat dangerous and thus only imported in packages
|
||||
// that import unsafe.
|
||||
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.nobounds = true
|
||||
}
|
||||
case "//go:noescape":
|
||||
@@ -546,9 +451,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
if len(f.Blocks) == 0 {
|
||||
info.noescape = true
|
||||
}
|
||||
case "//go:noheap":
|
||||
// Ensure this function does not allocate on the heap.
|
||||
info.noheap = true
|
||||
case "//go:variadic":
|
||||
// The //go:variadic pragma is emitted by the CGo preprocessing
|
||||
// pass for C variadic functions. This includes both explicit
|
||||
@@ -636,9 +538,9 @@ func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool {
|
||||
hasHostLayout = false // package structs added in go1.23
|
||||
}
|
||||
}
|
||||
for field := range typ.Fields() {
|
||||
ftyp := field.Type()
|
||||
if types.Unalias(ftyp).String() == "structs.HostLayout" {
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
ftyp := typ.Field(i).Type()
|
||||
if ftyp.String() == "structs.HostLayout" {
|
||||
hasHostLayout = true
|
||||
continue
|
||||
}
|
||||
@@ -669,8 +571,8 @@ func getParams(sig *types.Signature) []*types.Var {
|
||||
if sig.Recv() != nil {
|
||||
params = append(params, sig.Recv())
|
||||
}
|
||||
for v := range sig.Params().Variables() {
|
||||
params = append(params, v)
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
params = append(params, sig.Params().At(i))
|
||||
}
|
||||
return params
|
||||
}
|
||||
@@ -735,34 +637,6 @@ type globalInfo struct {
|
||||
section string // go:section
|
||||
}
|
||||
|
||||
// fileForFunc returns the *ast.File that contains the declaration of f, or
|
||||
// nil if it cannot be determined. File-level pragmas are only consulted for
|
||||
// functions in the package currently being compiled — functions imported from
|
||||
// other packages have their file-level pragmas processed when those packages
|
||||
// are compiled.
|
||||
func (c *compilerContext) fileForFunc(f *ssa.Function) *ast.File {
|
||||
if c.loaderPkg == nil || f.Pkg == nil || f.Pkg.Pkg != c.loaderPkg.Pkg {
|
||||
return nil
|
||||
}
|
||||
syntax := f.Syntax()
|
||||
if f.Origin() != nil {
|
||||
syntax = f.Origin().Syntax()
|
||||
}
|
||||
if syntax == nil {
|
||||
return nil
|
||||
}
|
||||
pos := syntax.Pos()
|
||||
if !pos.IsValid() {
|
||||
return nil
|
||||
}
|
||||
for _, file := range c.loaderPkg.Files {
|
||||
if file.FileStart <= pos && pos < file.FileEnd {
|
||||
return file
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||
// program. In particular, they are required for //go:extern pragmas on globals.
|
||||
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
|
||||
@@ -801,7 +675,10 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||
|
||||
// Set alignment from the //go:align comment.
|
||||
alignment := max(info.align, c.targetData.ABITypeAlignment(llvmType))
|
||||
alignment := c.targetData.ABITypeAlignment(llvmType)
|
||||
if info.align > alignment {
|
||||
alignment = info.align
|
||||
}
|
||||
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
||||
// Check for power-of-two (or 0).
|
||||
// See: https://stackoverflow.com/a/108360
|
||||
@@ -875,7 +752,7 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup, c *compilerContext,
|
||||
// This is a slightly looser requirement than what gc uses: gc
|
||||
// requires the file to import "unsafe", not the package as a
|
||||
// whole.
|
||||
if slices.Contains(g.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||
if hasUnsafeImport(g.Pkg.Pkg) {
|
||||
info.linkName = parts[2]
|
||||
}
|
||||
}
|
||||
@@ -891,3 +768,13 @@ func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
// Return true if this package imports "unsafe", false otherwise.
|
||||
func hasUnsafeImport(pkg *types.Package) bool {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp == types.Unsafe {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+27
-156
@@ -26,25 +26,24 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
argTypes := []llvm.Type{b.uintptrType}
|
||||
// Constraints will look something like:
|
||||
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={rax},0")
|
||||
constraints := "={rax},0"
|
||||
for i, arg := range call.Args[1:] {
|
||||
constraints.WriteString("," + [...]string{
|
||||
constraints += "," + [...]string{
|
||||
"{rdi}",
|
||||
"{rsi}",
|
||||
"{rdx}",
|
||||
"{r10}",
|
||||
"{r8}",
|
||||
"{r9}",
|
||||
}[i])
|
||||
}[i]
|
||||
llvmValue := b.getValue(arg, getPos(call))
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
// rcx and r11 are clobbered by the syscall, so make sure they are not used
|
||||
constraints.WriteString(",~{rcx},~{r11}")
|
||||
constraints += ",~{rcx},~{r11}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints.String(), true, false, llvm.InlineAsmDialectIntel, false)
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
|
||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||
@@ -56,23 +55,22 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
argTypes := []llvm.Type{b.uintptrType}
|
||||
// Constraints will look something like:
|
||||
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={eax},0")
|
||||
constraints := "={eax},0"
|
||||
for i, arg := range call.Args[1:] {
|
||||
constraints.WriteString("," + [...]string{
|
||||
constraints += "," + [...]string{
|
||||
"{ebx}",
|
||||
"{ecx}",
|
||||
"{edx}",
|
||||
"{esi}",
|
||||
"{edi}",
|
||||
"{ebp}",
|
||||
}[i])
|
||||
}[i]
|
||||
llvmValue := b.getValue(arg, getPos(call))
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints.String(), true, false, llvm.InlineAsmDialectIntel, false)
|
||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
|
||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||
@@ -90,10 +88,9 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
argTypes := []llvm.Type{}
|
||||
// Constraints will look something like:
|
||||
// ={r0},0,{r1},{r2},{r7},~{r3}
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={r0}")
|
||||
constraints := "={r0}"
|
||||
for i, arg := range call.Args[1:] {
|
||||
constraints.WriteString("," + [...]string{
|
||||
constraints += "," + [...]string{
|
||||
"0", // tie to output
|
||||
"{r1}",
|
||||
"{r2}",
|
||||
@@ -101,20 +98,20 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
"{r4}",
|
||||
"{r5}",
|
||||
"{r6}",
|
||||
}[i])
|
||||
}[i]
|
||||
llvmValue := b.getValue(arg, getPos(call))
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
args = append(args, num)
|
||||
argTypes = append(argTypes, b.uintptrType)
|
||||
constraints.WriteString(",{r7}") // syscall number
|
||||
constraints += ",{r7}" // syscall number
|
||||
for i := len(call.Args) - 1; i < 4; i++ {
|
||||
// r0-r3 get clobbered after the syscall returns
|
||||
constraints.WriteString(",~{r" + strconv.Itoa(i) + "}")
|
||||
constraints += ",~{r" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints.String(), true, false, 0, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
|
||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||
@@ -123,32 +120,31 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
argTypes := []llvm.Type{}
|
||||
// Constraints will look something like:
|
||||
// ={x0},0,{x1},{x2},{x8},~{x3},~{x4},~{x5},~{x6},~{x7},~{x16},~{x17}
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={x0}")
|
||||
constraints := "={x0}"
|
||||
for i, arg := range call.Args[1:] {
|
||||
constraints.WriteString("," + [...]string{
|
||||
constraints += "," + [...]string{
|
||||
"0", // tie to output
|
||||
"{x1}",
|
||||
"{x2}",
|
||||
"{x3}",
|
||||
"{x4}",
|
||||
"{x5}",
|
||||
}[i])
|
||||
}[i]
|
||||
llvmValue := b.getValue(arg, getPos(call))
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
args = append(args, num)
|
||||
argTypes = append(argTypes, b.uintptrType)
|
||||
constraints.WriteString(",{x8}") // syscall number
|
||||
constraints += ",{x8}" // syscall number
|
||||
for i := len(call.Args) - 1; i < 8; i++ {
|
||||
// x0-x7 may get clobbered during the syscall following the aarch64
|
||||
// calling convention.
|
||||
constraints.WriteString(",~{x" + strconv.Itoa(i) + "}")
|
||||
constraints += ",~{x" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
constraints.WriteString(",~{x16},~{x17}") // scratch registers
|
||||
constraints += ",~{x16},~{x17}" // scratch registers
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints.String(), true, false, 0, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
|
||||
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
|
||||
@@ -167,8 +163,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
// faster and smaller code.
|
||||
args := []llvm.Value{num}
|
||||
argTypes := []llvm.Type{b.uintptrType}
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={$2},={$7},0")
|
||||
constraints := "={$2},={$7},0"
|
||||
syscallParams := call.Args[1:]
|
||||
if len(syscallParams) > 7 {
|
||||
// There is one syscall that uses 7 parameters: sync_file_range.
|
||||
@@ -177,7 +172,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
syscallParams = syscallParams[:7]
|
||||
}
|
||||
for i, arg := range syscallParams {
|
||||
constraints.WriteString("," + [...]string{
|
||||
constraints += "," + [...]string{
|
||||
"{$4}", // arg1
|
||||
"{$5}", // arg2
|
||||
"{$6}", // arg3
|
||||
@@ -185,7 +180,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
"r", // arg5 on the stack
|
||||
"r", // arg6 on the stack
|
||||
"r", // arg7 on the stack
|
||||
}[i])
|
||||
}[i]
|
||||
llvmValue := b.getValue(arg, getPos(call))
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
@@ -226,10 +221,10 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
"addu $$sp, $$sp, 32\n" +
|
||||
".set at\n"
|
||||
}
|
||||
constraints.WriteString(",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}")
|
||||
constraints += ",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}"
|
||||
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
|
||||
fnType := llvm.FunctionType(returnType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, true, 0, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, true, 0, false)
|
||||
call := b.CreateCall(fnType, target, args, "")
|
||||
resultCode := b.CreateExtractValue(call, 0, "") // r2
|
||||
errorFlag := b.CreateExtractValue(call, 1, "") // r7
|
||||
@@ -354,130 +349,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// createSyscalln emits instructions for the syscall.syscalln function on
|
||||
// Windows. This handles the variadic calling convention used in Go 1.26+:
|
||||
//
|
||||
// func syscalln(fn, n uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
|
||||
//
|
||||
// The function generates a switch on n to dispatch to the correct fixed-argument
|
||||
// function pointer call with SetLastError(0)/GetLastError() wrapping.
|
||||
func (b *builder) createSyscalln(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
const maxArgs = 18 // Windows syscalls support up to 18 args
|
||||
|
||||
isI386 := strings.HasPrefix(b.Triple, "i386-")
|
||||
|
||||
// Get the function pointer (call.Args[0]) and n (call.Args[1]).
|
||||
fn := b.getValue(call.Args[0], getPos(call))
|
||||
fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "")
|
||||
n := b.getValue(call.Args[1], getPos(call))
|
||||
|
||||
// Get the variadic args slice (call.Args[2]).
|
||||
// In SSA, the variadic slice is the third argument.
|
||||
var argsPtr llvm.Value
|
||||
if len(call.Args) > 2 {
|
||||
argsSlice := b.getValue(call.Args[2], getPos(call))
|
||||
argsPtr = b.CreateExtractValue(argsSlice, 0, "args.data")
|
||||
} else {
|
||||
argsPtr = llvm.ConstNull(b.dataPtrType)
|
||||
}
|
||||
|
||||
// Prepare SetLastError and GetLastError.
|
||||
setLastError := b.mod.NamedFunction("SetLastError")
|
||||
if setLastError.IsNil() {
|
||||
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
||||
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
||||
if isI386 {
|
||||
setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||
}
|
||||
}
|
||||
getLastError := b.mod.NamedFunction("GetLastError")
|
||||
if getLastError.IsNil() {
|
||||
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
||||
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
||||
if isI386 {
|
||||
getLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||
}
|
||||
}
|
||||
|
||||
retType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)
|
||||
|
||||
// Create the merge block where all cases converge.
|
||||
mergeBB := b.insertBasicBlock("syscalln.merge")
|
||||
|
||||
// Create the default (panic) block.
|
||||
panicBB := b.insertBasicBlock("syscalln.panic")
|
||||
|
||||
// Create the switch on n.
|
||||
sw := b.CreateSwitch(n, panicBB, maxArgs+1)
|
||||
|
||||
// We'll collect blocks and values for the PHI node.
|
||||
var incomingVals []llvm.Value
|
||||
var incomingBlocks []llvm.BasicBlock
|
||||
|
||||
// Generate a case for each arg count 0..maxArgs.
|
||||
for i := 0; i <= maxArgs; i++ {
|
||||
caseBB := b.insertBasicBlock("syscalln.case" + strconv.Itoa(i))
|
||||
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), caseBB)
|
||||
b.SetInsertPointAtEnd(caseBB)
|
||||
|
||||
// Load args[0] through args[i-1] from the slice data pointer.
|
||||
var params []llvm.Value
|
||||
var paramTypes []llvm.Type
|
||||
for j := 0; j < i; j++ {
|
||||
gep := b.CreateInBoundsGEP(b.uintptrType, argsPtr, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(j), false),
|
||||
}, "")
|
||||
arg := b.CreateLoad(b.uintptrType, gep, "")
|
||||
params = append(params, arg)
|
||||
paramTypes = append(paramTypes, b.uintptrType)
|
||||
}
|
||||
|
||||
// SetLastError(0)
|
||||
setCall := b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||
var sp llvm.Value
|
||||
if isI386 {
|
||||
setCall.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||
sp = b.readStackPointer()
|
||||
}
|
||||
|
||||
// Call fn(args...)
|
||||
fnType := llvm.FunctionType(b.uintptrType, paramTypes, false)
|
||||
syscallResult := b.CreateCall(fnType, fnPtr, params, "")
|
||||
if isI386 {
|
||||
syscallResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||
b.writeStackPointer(sp)
|
||||
}
|
||||
|
||||
// err = GetLastError()
|
||||
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
||||
if isI386 {
|
||||
errResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||
}
|
||||
if b.uintptrType != b.ctx.Int32Type() {
|
||||
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||
}
|
||||
|
||||
// Build {r1, 0, err}
|
||||
result := llvm.ConstNull(retType)
|
||||
result = b.CreateInsertValue(result, syscallResult, 0, "")
|
||||
result = b.CreateInsertValue(result, errResult, 2, "")
|
||||
|
||||
incomingVals = append(incomingVals, result)
|
||||
incomingBlocks = append(incomingBlocks, b.Builder.GetInsertBlock())
|
||||
b.CreateBr(mergeBB)
|
||||
}
|
||||
|
||||
// Panic block for n > maxArgs.
|
||||
b.SetInsertPointAtEnd(panicBB)
|
||||
b.CreateUnreachable()
|
||||
|
||||
// Merge block: PHI node to select the result.
|
||||
b.SetInsertPointAtEnd(mergeBB)
|
||||
phi := b.CreatePHI(retType, "syscalln.result")
|
||||
phi.AddIncoming(incomingVals, incomingBlocks)
|
||||
return phi, nil
|
||||
}
|
||||
|
||||
// createRawSyscallNoError emits instructions for the Linux-specific
|
||||
// syscall.rawSyscallNoError function.
|
||||
func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
|
||||
Vendored
-5
@@ -66,11 +66,6 @@ func complexSub(x, y complex64) complex64 {
|
||||
return x - y
|
||||
}
|
||||
|
||||
func shiftNested(x uint64) uint64 {
|
||||
k := 3
|
||||
return x >> (1 << k) // https://github.com/tinygo-org/tinygo/issues/5496
|
||||
}
|
||||
|
||||
func complexMul(x, y complex64) complex64 {
|
||||
return x * y
|
||||
}
|
||||
|
||||
Vendored
+33
-36
@@ -10,30 +10,33 @@ target triple = "wasm32-unknown-wasi"
|
||||
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -47,14 +50,14 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.divideByZeroPanic(ptr) #0
|
||||
declare void @runtime.divideByZeroPanic(ptr) #1
|
||||
|
||||
; 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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -64,12 +67,12 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -83,12 +86,12 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
@@ -98,66 +101,66 @@ divbyzero.next: ; preds = %entry
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = fadd float %x.r, %y.r
|
||||
%1 = fadd float %x.i, %y.i
|
||||
@@ -167,7 +170,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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = fsub float %x.r, %y.r
|
||||
%1 = fsub float %x.i, %y.i
|
||||
@@ -177,14 +180,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i64 @main.shiftNested(i64 %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = lshr i64 %x, 8
|
||||
ret i64 %0
|
||||
}
|
||||
|
||||
; 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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = fmul float %x.r, %y.r
|
||||
%1 = fmul float %x.i, %y.i
|
||||
@@ -198,18 +194,19 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
Vendored
+25
-21
@@ -6,73 +6,76 @@ target triple = "wasm32-unknown-wasi"
|
||||
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
||||
%runtime.chanSelectState = type { ptr, ptr }
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
store i32 3, ptr %chan.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
||||
|
||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||
%select.send.value = alloca i32, align 4
|
||||
@@ -85,7 +88,7 @@ entry:
|
||||
store ptr %ch2, ptr %0, align 4
|
||||
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
|
||||
store ptr null, ptr %.repack3, align 4
|
||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #3
|
||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||
%2 = icmp eq i32 %1, 0
|
||||
@@ -102,9 +105,10 @@ select.body: ; preds = %select.next
|
||||
br label %select.done
|
||||
}
|
||||
|
||||
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #0
|
||||
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #3 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #4 = { nounwind }
|
||||
|
||||
+40
-37
@@ -3,29 +3,32 @@ source_filename = "defer.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface, ptr }
|
||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.external(ptr) #1
|
||||
declare void @main.external(ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferSimple(ptr %context) unnamed_addr #0 {
|
||||
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
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||
store i32 0, ptr %defer.alloca, align 4
|
||||
%defer.alloca.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||
store ptr %defer.next, ptr %defer.alloca.repack15, align 4
|
||||
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) #5
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
@@ -109,14 +112,14 @@ rundefers.end3: ; preds = %rundefers.loophead6
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
||||
declare ptr @llvm.stacksave.p0() #2
|
||||
declare ptr @llvm.stacksave.p0() #3
|
||||
|
||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(28), ptr, ptr) #1
|
||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2
|
||||
|
||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(28), ptr) #1
|
||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 {
|
||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printlock(ptr undef) #4
|
||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||
@@ -124,29 +127,29 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.printlock(ptr) #1
|
||||
declare void @runtime.printlock(ptr) #2
|
||||
|
||||
declare void @runtime.printint32(i32, ptr) #1
|
||||
declare void @runtime.printint32(i32, ptr) #2
|
||||
|
||||
declare void @runtime.printunlock(ptr) #1
|
||||
declare void @runtime.printunlock(ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #0 {
|
||||
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
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||
store i32 0, ptr %defer.alloca, align 4
|
||||
%defer.alloca.repack22 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||
store ptr %defer.next, ptr %defer.alloca.repack22, align 4
|
||||
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.repack24 = getelementptr inbounds nuw i8, ptr %defer.alloca2, i32 4
|
||||
store ptr %defer.alloca, ptr %defer.alloca2.repack24, align 4
|
||||
%defer.alloca2.repack23 = getelementptr inbounds nuw i8, ptr %defer.alloca2, i32 4
|
||||
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) #5
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
@@ -250,7 +253,7 @@ rundefers.end7: ; preds = %rundefers.loophead1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #0 {
|
||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printlock(ptr undef) #4
|
||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||
@@ -259,7 +262,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #0 {
|
||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printlock(ptr undef) #4
|
||||
call void @runtime.printint32(i32 5, ptr undef) #4
|
||||
@@ -268,17 +271,18 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
br label %for.body
|
||||
|
||||
for.body: ; preds = %for.body, %entry
|
||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||
%defer.alloc.call = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 135 to ptr), ptr undef) #4
|
||||
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
|
||||
store i32 0, ptr %defer.alloc.call, align 4
|
||||
%defer.alloc.call.repack1 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
|
||||
store ptr %defer.next, ptr %defer.alloc.call.repack1, align 4
|
||||
@@ -311,14 +315,12 @@ rundefers.end: ; preds = %rundefers.loophead
|
||||
br label %recover
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferLoop(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
br label %for.loop
|
||||
@@ -330,7 +332,7 @@ for.loop: ; preds = %for.body, %entry
|
||||
|
||||
for.body: ; preds = %for.loop
|
||||
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||
%defer.alloc.call = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 135 to ptr), ptr undef) #4
|
||||
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
|
||||
store i32 0, ptr %defer.alloc.call, align 4
|
||||
%defer.alloc.call.repack13 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
|
||||
store ptr %defer.next, ptr %defer.alloc.call.repack13, align 4
|
||||
@@ -403,11 +405,12 @@ rundefers.end1: ; preds = %rundefers.loophead4
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%defer.alloca = alloca { i32, ptr, i32 }, align 4
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||
%0 = call ptr @llvm.stacksave.p0()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||
br label %for.loop
|
||||
@@ -502,9 +505,9 @@ rundefers.end4: ; preds = %rundefers.loophead7
|
||||
br label %recover
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #2 = { nocallback nofree nosync nounwind willreturn }
|
||||
attributes #3 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #3 = { nocallback nofree nosync nounwind willreturn }
|
||||
attributes #4 = { nounwind }
|
||||
attributes #5 = { nounwind returns_twice }
|
||||
|
||||
Vendored
+16
-12
@@ -3,16 +3,19 @@ source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -24,25 +27,25 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 {
|
||||
define hidden float @main.maxu32f(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret float 0x41F0000000000000
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #2 {
|
||||
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(ptr %context) unnamed_addr #2 {
|
||||
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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = uitofp i32 %v to float
|
||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||
@@ -52,7 +55,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 {
|
||||
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -65,7 +68,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 {
|
||||
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||
@@ -77,7 +80,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 {
|
||||
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||
@@ -90,5 +93,6 @@ entry:
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
|
||||
Vendored
+15
-11
@@ -3,44 +3,48 @@ source_filename = "func.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
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(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = icmp eq ptr %callback.funcptr, null
|
||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||
|
||||
fpcall.next: ; preds = %entry
|
||||
call void %callback.funcptr(i32 3, ptr %callback.context) #2
|
||||
call void %callback.funcptr(i32 3, ptr %callback.context) #3
|
||||
ret void
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(ptr undef) #2
|
||||
call void @runtime.nilPanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.nilPanic(ptr) #0
|
||||
declare void @runtime.nilPanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.bar(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
Vendored
-8
@@ -24,10 +24,6 @@ var (
|
||||
x *byte
|
||||
y [61]uintptr
|
||||
}
|
||||
struct5 *struct {
|
||||
x *byte
|
||||
y [30]uintptr
|
||||
}
|
||||
|
||||
slice1 []byte
|
||||
slice2 []*int
|
||||
@@ -62,10 +58,6 @@ func newStruct() {
|
||||
x *byte
|
||||
y [61]uintptr
|
||||
})
|
||||
struct5 = new(struct {
|
||||
x *byte
|
||||
y [30]uintptr
|
||||
})
|
||||
}
|
||||
|
||||
func newFuncValue() *func() {
|
||||
|
||||
Vendored
+20
-27
@@ -16,25 +16,27 @@ target triple = "wasm32-unknown-wasi"
|
||||
@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.struct5 = hidden global ptr null, align 4
|
||||
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||
@"runtime/gc.layout:62-0100000000000020" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||
@"runtime/gc.layout:62-0100000000000000" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||
@"runtime/gc.layout:62-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 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
@@ -52,11 +54,8 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
@@ -72,32 +71,26 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 1 ptr @runtime.alloc_zero(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
%new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store ptr %new, ptr @main.struct1, align 4
|
||||
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store ptr %new1, ptr @main.struct2, align 4
|
||||
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000020", ptr undef) #3
|
||||
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store ptr %new2, ptr @main.struct3, align 4
|
||||
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000000", ptr undef) #3
|
||||
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store ptr %new3, ptr @main.struct4, align 4
|
||||
%new4 = call align 4 dereferenceable(124) ptr @runtime.alloc(i32 124, ptr nonnull inttoptr (i32 127 to ptr), ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr nonnull %new4, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store ptr %new4, ptr @main.struct5, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc_zero(i32, ptr, ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
|
||||
@@ -106,7 +99,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
@@ -128,10 +121,10 @@ entry:
|
||||
}
|
||||
|
||||
; 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, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3
|
||||
store double %v.r, ptr %0, align 8
|
||||
%.repack1 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||
@@ -142,7 +135,7 @@ entry:
|
||||
ret %runtime._interface %1
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
Vendored
-37
@@ -19,49 +19,12 @@ func Add[T Coord](a, b Point[T]) Point[T] {
|
||||
}
|
||||
}
|
||||
|
||||
func aliasSize[F float32 | float64]() uintptr {
|
||||
return unsafe.Sizeof(F(0))
|
||||
}
|
||||
|
||||
func aliasSize32() uintptr {
|
||||
type F = float32
|
||||
return aliasSize[F]()
|
||||
}
|
||||
|
||||
func aliasSize64() uintptr {
|
||||
type F = float64
|
||||
return aliasSize[F]()
|
||||
}
|
||||
|
||||
type aliasMethodResult[F float32 | float64] struct {
|
||||
Value F
|
||||
}
|
||||
|
||||
type aliasMethodValue[F float32 | float64] struct{}
|
||||
|
||||
func (aliasMethodValue[F]) Get() aliasMethodResult[F] {
|
||||
return aliasMethodResult[F]{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var af, bf Point[float32]
|
||||
Add(af, bf)
|
||||
|
||||
var ai, bi Point[int]
|
||||
Add(ai, bi)
|
||||
|
||||
checkSize(aliasSize32())
|
||||
checkSize(aliasSize64())
|
||||
}
|
||||
|
||||
func checkSize(uintptr)
|
||||
|
||||
func checkBool(bool)
|
||||
|
||||
func aliasMethod32(x any) {
|
||||
type F = float32
|
||||
_, ok := x.(interface {
|
||||
Get() aliasMethodResult[F]
|
||||
})
|
||||
checkBool(ok)
|
||||
}
|
||||
|
||||
Vendored
+212
-149
@@ -1,196 +1,259 @@
|
||||
; ModuleID = 'generics.go'
|
||||
source_filename = "generics.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target 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[float32]" = type { float, float }
|
||||
%"main.Point[int]" = type { i32, i32 }
|
||||
%"main.Point[float32]" = type { float, float }
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
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(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.aliasSize32(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.main(i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i32 @"main.aliasSize[basic:float32]"(ptr undef)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr hidden i32 @"main.aliasSize[basic:float32]"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 4
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.aliasSize64(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i32 @"main.aliasSize[basic:float64]"(ptr undef)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr hidden i32 @"main.aliasSize[basic:float64]"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 8
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call %"main.Point[float32]" @"main.Add[basic:float32]"(float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, ptr undef)
|
||||
%1 = call %"main.Point[int]" @"main.Add[basic:int]"(i32 0, i32 0, i32 0, i32 0, ptr undef)
|
||||
%2 = call i32 @main.aliasSize32(ptr undef)
|
||||
call void @main.checkSize(i32 %2, ptr undef) #4
|
||||
%3 = call i32 @main.aliasSize64(ptr undef)
|
||||
call void @main.checkSize(i32 %3, ptr undef) #4
|
||||
%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[basic:float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, ptr %context) unnamed_addr #1 {
|
||||
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #4
|
||||
store float %a.X, ptr %a, align 4
|
||||
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
store float %a.Y, ptr %a.repack5, align 4
|
||||
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #4
|
||||
store float %b.X, ptr %b, align 4
|
||||
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
store float %b.Y, ptr %b.repack7, align 4
|
||||
call void @main.checkSize(i32 4, ptr undef) #4
|
||||
call void @main.checkSize(i32 8, ptr undef) #4
|
||||
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4
|
||||
%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.throw, label %deref.next1
|
||||
br i1 false, label %deref.throw1, label %deref.next2
|
||||
|
||||
deref.next1: ; preds = %deref.next
|
||||
%0 = load float, ptr %a, align 4
|
||||
%1 = load float, ptr %b, align 4
|
||||
%2 = fadd float %0, %1
|
||||
br i1 false, label %deref.throw, label %deref.next2
|
||||
deref.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.next2: ; preds = %deref.next1
|
||||
br i1 false, label %deref.throw, label %deref.next3
|
||||
deref.next4: ; preds = %deref.next2
|
||||
br i1 false, label %deref.throw5, label %deref.next6
|
||||
|
||||
deref.next3: ; preds = %deref.next2
|
||||
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
%5 = load float, ptr %4, align 4
|
||||
%6 = load float, ptr %3, align 4
|
||||
br i1 false, label %deref.throw, label %store.next
|
||||
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.next3
|
||||
store float %2, ptr %complit, align 4
|
||||
br i1 false, label %deref.throw, label %store.next4
|
||||
store.next: ; preds = %deref.next6
|
||||
store float %8, float* %3, align 8
|
||||
br i1 false, label %store.throw7, label %store.next8
|
||||
|
||||
store.next4: ; preds = %store.next
|
||||
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||
%8 = fadd float %5, %6
|
||||
store float %8, ptr %7, align 4
|
||||
%.unpack = load float, ptr %complit, align 4
|
||||
%9 = insertvalue %"main.Point[float32]" poison, float %.unpack, 0
|
||||
%10 = insertvalue %"main.Point[float32]" %9, float %8, 1
|
||||
ret %"main.Point[float32]" %10
|
||||
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 = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
|
||||
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
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||
declare void @main.checkSize(i32, i8*) #0
|
||||
|
||||
declare void @main.checkSize(i32, ptr) #0
|
||||
|
||||
declare void @runtime.nilPanic(ptr) #0
|
||||
declare void @runtime.nilPanic(i8*) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr hidden %"main.Point[int]" @"main.Add[basic:int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 {
|
||||
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, i8* %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #4
|
||||
store i32 %a.X, ptr %a, align 4
|
||||
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
store i32 %a.Y, ptr %a.repack5, align 4
|
||||
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #4
|
||||
store i32 %b.X, ptr %b, align 4
|
||||
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
store i32 %b.Y, ptr %b.repack7, align 4
|
||||
call void @main.checkSize(i32 4, ptr undef) #4
|
||||
call void @main.checkSize(i32 8, ptr undef) #4
|
||||
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4
|
||||
%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.throw, label %deref.next1
|
||||
br i1 false, label %deref.throw1, label %deref.next2
|
||||
|
||||
deref.next1: ; preds = %deref.next
|
||||
%0 = load i32, ptr %a, align 4
|
||||
%1 = load i32, ptr %b, align 4
|
||||
%2 = add i32 %0, %1
|
||||
br i1 false, label %deref.throw, label %deref.next2
|
||||
deref.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.next2: ; preds = %deref.next1
|
||||
br i1 false, label %deref.throw, label %deref.next3
|
||||
deref.next4: ; preds = %deref.next2
|
||||
br i1 false, label %deref.throw5, label %deref.next6
|
||||
|
||||
deref.next3: ; preds = %deref.next2
|
||||
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||
%5 = load i32, ptr %4, align 4
|
||||
%6 = load i32, ptr %3, align 4
|
||||
br i1 false, label %deref.throw, label %store.next
|
||||
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.next3
|
||||
store i32 %2, ptr %complit, align 4
|
||||
br i1 false, label %deref.throw, label %store.next4
|
||||
store.next: ; preds = %deref.next6
|
||||
store i32 %8, i32* %3, align 8
|
||||
br i1 false, label %store.throw7, label %store.next8
|
||||
|
||||
store.next4: ; preds = %store.next
|
||||
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||
%8 = add i32 %5, %6
|
||||
store i32 %8, ptr %7, align 4
|
||||
%.unpack = load i32, ptr %complit, align 4
|
||||
%9 = insertvalue %"main.Point[int]" poison, i32 %.unpack, 0
|
||||
%10 = insertvalue %"main.Point[int]" %9, i32 %8, 1
|
||||
ret %"main.Point[int]" %10
|
||||
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 = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
|
||||
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.checkBool(i1, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.aliasMethod32(ptr %x.typecode, ptr %x.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[basic:float32]}}.$typeassert"(ptr %x.typecode) #4
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
call void @main.checkBool(i1 %0, ptr undef) #4
|
||||
ret void
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[basic:float32]}}.$typeassert"(ptr) #3
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Get:func:{}{named:main.aliasMethodResult[basic:float32]}" }
|
||||
attributes #4 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+17
-13
@@ -5,24 +5,27 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime._string = type { ptr, i32 }
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
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 {
|
||||
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
||||
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 {
|
||||
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp slt i16 %len, 0
|
||||
@@ -36,24 +39,25 @@ unsafe.String.next: ; preds = %entry
|
||||
%5 = zext nneg i16 %len to i32
|
||||
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
||||
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
||||
ret %runtime._string %7
|
||||
|
||||
unsafe.String.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
||||
ret ptr %s.data
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
Vendored
+43
-39
@@ -5,32 +5,35 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime._string = type { ptr, i32 }
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.smin.i32(i32, i32) #2
|
||||
declare i32 @llvm.smin.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||
@@ -38,7 +41,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||
@@ -47,109 +50,109 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i8 @llvm.umin.i8(i8, i8) #2
|
||||
declare i8 @llvm.umin.i8(i8, i8) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.umin.i32(i32, i32) #2
|
||||
declare i32 @llvm.umin.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call float @llvm.minimum.f32(float %a, float %b)
|
||||
ret float %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare float @llvm.minimum.f32(float, float) #2
|
||||
declare float @llvm.minimum.f32(float, float) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call double @llvm.minimum.f64(double %a, double %b)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare double @llvm.minimum.f64(double, double) #2
|
||||
declare double @llvm.minimum.f64(double, double) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||
%stackalloc = alloca i8, align 1
|
||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #4
|
||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.smax.i32(i32, i32) #2
|
||||
declare i32 @llvm.smax.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.umax.i32(i32, i32) #2
|
||||
declare i32 @llvm.umax.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
|
||||
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call float @llvm.maximum.f32(float %a, float %b)
|
||||
ret float %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare float @llvm.maximum.f32(float, float) #2
|
||||
declare float @llvm.maximum.f32(float, float) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||
%stackalloc = alloca i8, align 1
|
||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #4
|
||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = shl i32 %s.len, 2
|
||||
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
||||
@@ -157,25 +160,26 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
|
||||
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
|
||||
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearMap(ptr dereferenceable_or_null(48) %m, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @runtime.hashmapClear(ptr %m, ptr undef) #4
|
||||
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(48), ptr) #0
|
||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||
attributes #4 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
Vendored
-24
@@ -1,24 +0,0 @@
|
||||
package main
|
||||
|
||||
// genericMethod has both a regular method and a "generic method": a method
|
||||
// with its own type parameter, independent of any type parameter on the
|
||||
// receiver. This is a Go 1.27 feature (see math/rand/v2.Rand.N for a
|
||||
// real-world example). Boxing such a value into an interface must not try to
|
||||
// encode the generic method's type-parameterized signature into a type code.
|
||||
type genericMethod struct{}
|
||||
|
||||
func (t genericMethod) Regular(n int) int { return n }
|
||||
|
||||
func (t genericMethod) GenericParam[X int | int64](n X) X { return n }
|
||||
|
||||
// onlyGenericMethod's only method is generic, so its runtime type must have
|
||||
// an empty method set (hasMethodSet must become false, not just numMethods).
|
||||
type onlyGenericMethod struct{}
|
||||
|
||||
func (t onlyGenericMethod) GenericParam[X int | int64](n X) X { return n }
|
||||
|
||||
func useGenericMethods() (any, any) {
|
||||
var g genericMethod
|
||||
var o onlyGenericMethod
|
||||
return any(g), any(o)
|
||||
}
|
||||
Vendored
-67
@@ -1,67 +0,0 @@
|
||||
; ModuleID = 'go1.27.go'
|
||||
source_filename = "go1.27.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.structField = type { ptr, ptr }
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
|
||||
@"reflect/types.signature:Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] } { ptr @"named:main.genericMethod$methodset", i8 122, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, { i32, [1 x ptr] } }, ptr @"reflect/types.type:pointer:named:main.genericMethod", i32 0, i32 1), ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] }, [19 x i8] c"main.genericMethod\00" }, align 4
|
||||
@"reflect/types.type.pkgpath:main" = linkonce_odr unnamed_addr constant [5 x i8] c"main\00", align 1
|
||||
@"reflect/types.type:pointer:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, { i32, [1 x ptr] } } { ptr @"pointer:named:main.genericMethod$methodset", i8 -43, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] } }, align 4
|
||||
@"reflect/methods.Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
|
||||
@"main$string" = internal unnamed_addr constant [18 x i8] c"main.genericMethod", align 1
|
||||
@"main$string.1" = internal unnamed_addr constant [7 x i8] c"Regular", align 1
|
||||
@"pointer:named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(*main.genericMethod).Regular" } }
|
||||
@"reflect/types.type:struct:{}" = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, [0 x %runtime.structField] } { i8 90, i16 0, ptr @"reflect/types.type:pointer:struct:{}", ptr @"reflect/types.type.pkgpath.empty", i32 0, i16 0, [0 x %runtime.structField] zeroinitializer }, align 4
|
||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||
@"reflect/types.type:pointer:struct:{}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:struct:{}" }, align 4
|
||||
@"named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(main.genericMethod).Regular$invoke" } }
|
||||
@"reflect/types.type:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [23 x i8] } { i8 122, i16 0, ptr @"reflect/types.type:pointer:named:main.onlyGenericMethod", ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", [23 x i8] c"main.onlyGenericMethod\00" }, align 4
|
||||
@"reflect/types.type:pointer:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:main.onlyGenericMethod" }, align 4
|
||||
|
||||
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 i32 @"(main.genericMethod).Regular"(i32 %n, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 %n
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { %runtime._interface, %runtime._interface } @main.useGenericMethods(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull getelementptr inbounds nuw (i8, ptr @"reflect/types.type:named:main.genericMethod", i32 4), ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:named:main.onlyGenericMethod", ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
|
||||
ret { %runtime._interface, %runtime._interface } { %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), ptr null }, %runtime._interface { ptr @"reflect/types.type:named:main.onlyGenericMethod", ptr null } }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr hidden i32 @"(*main.genericMethod).Regular"(ptr %t, i32 %n, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %t, ptr nonnull %stackalloc, ptr undef) #2
|
||||
%0 = call i32 @"(main.genericMethod).Regular"(i32 %n, ptr undef)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr i32 @"(main.genericMethod).Regular$invoke"(ptr %0, i32 %1, ptr %2) unnamed_addr #1 {
|
||||
entry:
|
||||
%ret = call i32 @"(main.genericMethod).Regular"(i32 %1, ptr %2)
|
||||
ret i32 %ret
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
+32
-32
@@ -5,36 +5,39 @@ target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, ptr) #1
|
||||
declare void @main.regularFunction(i32, ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #1
|
||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #2
|
||||
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
||||
@@ -42,13 +45,13 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||
@@ -56,11 +59,11 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
||||
store i32 3, ptr %n, align 4
|
||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 133 to ptr), ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11
|
||||
store i32 5, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
store ptr %n, ptr %1, align 4
|
||||
@@ -73,11 +76,8 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
store i32 7, ptr %context, align 4
|
||||
ret void
|
||||
@@ -93,16 +93,16 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.printlock(ptr) #1
|
||||
declare void @runtime.printlock(ptr) #2
|
||||
|
||||
declare void @runtime.printint32(i32, ptr) #1
|
||||
declare void @runtime.printint32(i32, ptr) #2
|
||||
|
||||
declare void @runtime.printunlock(ptr) #1
|
||||
declare void @runtime.printunlock(ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 391 to ptr), ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
||||
store i32 5, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
store ptr %fn.context, ptr %1, align 4
|
||||
@@ -126,13 +126,13 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||
@@ -146,18 +146,18 @@ declare i32 @llvm.umin.i32(i32, i32) #7
|
||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #0 {
|
||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 713 to ptr), ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
||||
store ptr %itf.value, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
store ptr @"main$string", ptr %1, align 4
|
||||
@@ -186,15 +186,15 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #2 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print:func:{basic:string}{}" "tinygo-methods"="reflect/methods.Print:func:{basic:string}{}" }
|
||||
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||
attributes #11 = { nounwind }
|
||||
|
||||
+33
-33
@@ -5,27 +5,30 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.regularFunction(i32, ptr) #0
|
||||
declare void @main.regularFunction(i32, ptr) #1
|
||||
|
||||
declare void @runtime.deadlock(ptr) #0
|
||||
declare void @runtime.deadlock(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||
@@ -33,23 +36,23 @@ entry:
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
||||
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"(ptr %0) unnamed_addr #4 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||
@@ -58,7 +61,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
||||
@@ -66,7 +69,7 @@ entry:
|
||||
store i32 3, ptr %n, align 4
|
||||
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11
|
||||
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 133 to ptr), ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
||||
store i32 5, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
@@ -79,11 +82,8 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
store i32 7, ptr %context, align 4
|
||||
ret void
|
||||
@@ -100,17 +100,17 @@ entry:
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.printlock(ptr) #0
|
||||
declare void @runtime.printlock(ptr) #1
|
||||
|
||||
declare void @runtime.printint32(i32, ptr) #0
|
||||
declare void @runtime.printint32(i32, ptr) #1
|
||||
|
||||
declare void @runtime.printunlock(ptr) #0
|
||||
declare void @runtime.printunlock(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 391 to ptr), ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
||||
store i32 5, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
@@ -135,13 +135,13 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||
@@ -155,19 +155,19 @@ declare i32 @llvm.umin.i32(i32, i32) #7
|
||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #0
|
||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 713 to ptr), ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
||||
store ptr %itf.value, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
@@ -197,15 +197,15 @@ entry:
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #4 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print:func:{basic:string}{}" "tinygo-methods"="reflect/methods.Print:func:{basic:string}{}" }
|
||||
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||
attributes #11 = { nounwind }
|
||||
|
||||
Vendored
+44
-42
@@ -9,64 +9,65 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [7 x i8] } { i8 116, i16 -32767, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, 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, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
|
||||
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
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
|
||||
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
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
|
||||
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
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
|
||||
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
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -76,12 +77,12 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -91,12 +92,12 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -106,33 +107,34 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
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"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
|
||||
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"(ptr, i32, ptr, ptr) #5
|
||||
|
||||
; 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(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
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"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
|
||||
%1 = extractvalue %runtime._string %0, 0
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
|
||||
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"(ptr, ptr, ptr) #6
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error:func:{}{basic:string}" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String:func:{}{basic:string}" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo:func:{basic:int}{basic:uint8}" "tinygo-methods"="reflect/methods.String:func:{}{basic:string}; main.$methods.foo:func:{basic:int}{basic:uint8}" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error:func:{}{basic:string}" "tinygo-methods"="reflect/methods.Error:func:{}{basic:string}" }
|
||||
attributes #6 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #7 = { nounwind }
|
||||
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
package main
|
||||
|
||||
type largeOptimizedValue [1025]byte
|
||||
|
||||
type mixedLargeOptimizedValue struct {
|
||||
value [1025]byte
|
||||
any any
|
||||
}
|
||||
|
||||
func makeLargeOptimizedValue() largeOptimizedValue {
|
||||
return largeOptimizedValue{}
|
||||
}
|
||||
|
||||
func readLargeOptimizedValue(value largeOptimizedValue) byte {
|
||||
return value[len(value)-1]
|
||||
}
|
||||
|
||||
func readMixedLargeOptimizedValue(value mixedLargeOptimizedValue) byte {
|
||||
return value.value[len(value.value)-1]
|
||||
}
|
||||
Vendored
-120
@@ -1,120 +0,0 @@
|
||||
package main
|
||||
|
||||
type largeValue [1025]byte
|
||||
|
||||
type largeStruct struct {
|
||||
value [1025]byte
|
||||
ptr *byte
|
||||
}
|
||||
|
||||
type largeInterface interface {
|
||||
makeLargeValue() largeValue
|
||||
readLargeValue(largeValue) byte
|
||||
}
|
||||
|
||||
type largeReceiver largeValue
|
||||
|
||||
func makeLargeValue(value byte) largeValue {
|
||||
var result largeValue
|
||||
result[len(result)-1] = value
|
||||
return result
|
||||
}
|
||||
|
||||
func makeZeroLargeValue() largeValue {
|
||||
return largeValue{}
|
||||
}
|
||||
|
||||
func passZeroLargeValue() byte {
|
||||
return readLargeValue(largeValue{})
|
||||
}
|
||||
|
||||
func readLargeValue(value largeValue) byte {
|
||||
return value[len(value)-1]
|
||||
}
|
||||
|
||||
func useLargeValue() byte {
|
||||
return readLargeValue(makeLargeValue(42))
|
||||
}
|
||||
|
||||
func useLargeFunctionValue(fn func(largeValue) byte) byte {
|
||||
return fn(makeLargeValue(42))
|
||||
}
|
||||
|
||||
func (receiver largeReceiver) makeLargeValue() largeValue {
|
||||
return largeValue(receiver)
|
||||
}
|
||||
|
||||
func (receiver largeReceiver) readLargeValue(value largeValue) byte {
|
||||
return value[len(value)-1]
|
||||
}
|
||||
|
||||
func useLargeInterface(value largeInterface) byte {
|
||||
return value.readLargeValue(value.makeLargeValue())
|
||||
}
|
||||
|
||||
func deferLargeValue(value largeValue) {
|
||||
defer readLargeValue(value)
|
||||
}
|
||||
|
||||
func goLargeValue(value largeValue) {
|
||||
go readLargeValue(value)
|
||||
}
|
||||
|
||||
func makeLargeResults(value byte) (largeValue, byte) {
|
||||
return makeLargeValue(value), value
|
||||
}
|
||||
|
||||
func makeTwoLargeResults(value byte) (largeValue, largeValue) {
|
||||
return makeLargeValue(value), makeLargeValue(value + 1)
|
||||
}
|
||||
|
||||
func makeMixedLargeResults(value byte) (largeValue, byte, largeValue) {
|
||||
return makeLargeValue(value), value + 1, makeLargeValue(value + 2)
|
||||
}
|
||||
|
||||
func chooseLargeValue(flag bool) largeValue {
|
||||
value := makeLargeValue(1)
|
||||
if flag {
|
||||
value = makeLargeValue(42)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func makePointerLargeValue(value *byte) largeStruct {
|
||||
return largeStruct{ptr: value}
|
||||
}
|
||||
|
||||
func assertLargeValue(value any) byte {
|
||||
large, ok := value.(largeValue)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return large[len(large)-1]
|
||||
}
|
||||
|
||||
func useLargeMap(key, value largeValue) byte {
|
||||
values := map[largeValue]largeValue{key: value}
|
||||
result, ok := values[key]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return result[len(result)-1]
|
||||
}
|
||||
|
||||
func useLargeChannel(ch chan largeValue, value largeValue) byte {
|
||||
ch <- value
|
||||
result, ok := <-ch
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return result[len(result)-1]
|
||||
}
|
||||
|
||||
func selectLargeChannel(ch chan largeValue, value largeValue) byte {
|
||||
select {
|
||||
case ch <- value:
|
||||
return 0
|
||||
case result := <-ch:
|
||||
return result[len(result)-1]
|
||||
}
|
||||
}
|
||||
Vendored
-498
@@ -1,498 +0,0 @@
|
||||
; ModuleID = 'large.go'
|
||||
source_filename = "large.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime._string = type { ptr, i32 }
|
||||
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
||||
%runtime.chanSelectState = type { ptr, ptr }
|
||||
|
||||
@"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002" = linkonce_odr unnamed_addr constant { i32, [33 x i8] } { i32 258, [33 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\02" }
|
||||
@"reflect/types.typeid:named:main.largeValue" = external constant i8
|
||||
@llvm.used = appending global [15 x ptr] [ptr @"(main.largeReceiver).makeLargeValue", ptr @"(main.largeReceiver).readLargeValue", ptr @main.makeLargeValue, ptr @main.makeZeroLargeValue, ptr @main.readLargeValue, ptr @main.deferLargeValue, ptr @main.goLargeValue, ptr @main.makeLargeResults, ptr @main.makeTwoLargeResults, ptr @main.makeMixedLargeResults, ptr @main.chooseLargeValue, ptr @main.makePointerLargeValue, ptr @main.useLargeMap, ptr @main.useLargeChannel, ptr @main.selectLargeChannel]
|
||||
@"main$string" = internal unnamed_addr constant [31 x i8] c"blocking select matched no case", align 1
|
||||
@"main$pack" = internal unnamed_addr constant { %runtime._string } { %runtime._string { ptr @"main$string", i32 31 } }
|
||||
@"reflect/types.type:basic:string" = linkonce_odr constant { i8, ptr } { i8 81, ptr @"reflect/types.type:pointer:basic:string" }, align 4
|
||||
@"reflect/types.type:pointer:basic:string" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:string" }, align 4
|
||||
|
||||
declare void @runtime.trackPointer(ptr readonly captures(none), ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"(main.largeReceiver).makeLargeValue"(ptr dereferenceable_or_null(1025) %return, ptr readonly dereferenceable_or_null(1025) %receiver, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %receiver, i32 1025, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.memcpy.p0.p0.i32(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i32, i1 immarg) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @"(main.largeReceiver).readLargeValue"(ptr readonly dereferenceable_or_null(1025) %receiver, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%value1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %value1, ptr noundef nonnull align 1 dereferenceable(1025) %value, i32 1025, i1 false)
|
||||
%0 = getelementptr inbounds nuw i8, ptr %value1, i32 1024
|
||||
%1 = load i8, ptr %0, align 1
|
||||
ret i8 %1
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeLargeValue(ptr dereferenceable_or_null(1025) %return, i8 %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
%0 = getelementptr inbounds nuw i8, ptr %result, i32 1024
|
||||
store i8 %value, ptr %0, align 1
|
||||
%1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %1, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %1, ptr noundef nonnull align 1 dereferenceable(1025) %result, i32 1025, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %1, i32 1025, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeZeroLargeValue(ptr dereferenceable_or_null(1025) %return, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
store [1025 x i8] zeroinitializer, ptr %return, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.passZeroLargeValue(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%"main.largeValue{}:main.largeValue" = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %"main.largeValue{}:main.largeValue", ptr nonnull %stackalloc, ptr undef) #9
|
||||
store [1025 x i8] zeroinitializer, ptr %"main.largeValue{}:main.largeValue", align 1
|
||||
%0 = call i8 @main.readLargeValue(ptr nonnull %"main.largeValue{}:main.largeValue", ptr undef)
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.readLargeValue(ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%value1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %value1, ptr noundef nonnull align 1 dereferenceable(1025) %value, i32 1025, i1 false)
|
||||
%0 = getelementptr inbounds nuw i8, ptr %value1, i32 1024
|
||||
%1 = load i8, ptr %0, align 1
|
||||
ret i8 %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.useLargeValue(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result, i8 42, ptr undef)
|
||||
%0 = call i8 @main.readLargeValue(ptr nonnull %call.result, ptr undef)
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.useLargeFunctionValue(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result, i8 42, ptr undef)
|
||||
%0 = icmp eq ptr %fn.funcptr, null
|
||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||
|
||||
fpcall.next: ; preds = %entry
|
||||
%1 = call i8 %fn.funcptr(ptr nonnull %call.result, ptr %fn.context) #9
|
||||
ret i8 %1
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(ptr undef) #9
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.nilPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.useLargeInterface(ptr %value.typecode, ptr %value.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.makeLargeValue$invoke"(ptr nonnull %call.result, ptr %value.value, ptr %value.typecode, ptr undef) #9
|
||||
%0 = call i8 @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.readLargeValue$invoke"(ptr %value.value, ptr nonnull %call.result, ptr %value.typecode, ptr undef) #9
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
declare void @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.makeLargeValue$invoke"(ptr, ptr, ptr, ptr) #4
|
||||
|
||||
declare i8 @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.readLargeValue$invoke"(ptr, ptr, ptr, ptr) #5
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferLargeValue(ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%defer.alloca = alloca { i32, ptr, ptr }, align 8
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull %defer.alloca, ptr nonnull %stackalloc, ptr undef) #9
|
||||
store i32 0, ptr %defer.alloca, align 4
|
||||
%defer.alloca.repack1 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||
store ptr null, ptr %defer.alloca.repack1, align 4
|
||||
%defer.alloca.repack3 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 8
|
||||
store ptr %value, ptr %defer.alloca.repack3, align 4
|
||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||
br label %rundefers.block
|
||||
|
||||
rundefers.after: ; preds = %rundefers.end
|
||||
ret void
|
||||
|
||||
rundefers.block: ; preds = %entry
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
|
||||
%0 = load ptr, ptr %deferPtr, align 4
|
||||
%stackIsNil = icmp eq ptr %0, null
|
||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||
|
||||
rundefers.loop: ; preds = %rundefers.loophead
|
||||
%stack.next.gep = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||
store ptr %stack.next, ptr %deferPtr, align 4
|
||||
%callback = load i32, ptr %0, align 4
|
||||
switch i32 %callback, label %rundefers.default [
|
||||
i32 0, label %rundefers.callback0
|
||||
]
|
||||
|
||||
rundefers.callback0: ; preds = %rundefers.loop
|
||||
%gep = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||
%param = load ptr, ptr %gep, align 4
|
||||
%1 = call i8 @main.readLargeValue(ptr %param, ptr undef)
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.default: ; preds = %rundefers.loop
|
||||
unreachable
|
||||
|
||||
rundefers.end: ; preds = %rundefers.loophead
|
||||
br label %rundefers.after
|
||||
|
||||
recover: ; No predecessors!
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.goLargeValue(ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%go.param = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %go.param, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %go.param, ptr noundef nonnull align 1 dereferenceable(1025) %value, i32 1025, i1 false)
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.readLargeValue$gowrapper" to i32), ptr nonnull %go.param, i32 65536, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.deadlock(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"main.readLargeValue$gowrapper"(ptr %0) unnamed_addr #6 {
|
||||
entry:
|
||||
%1 = call i8 @main.readLargeValue(ptr %0, ptr undef)
|
||||
call void @runtime.deadlock(ptr undef) #9
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeLargeResults(ptr dereferenceable_or_null(1026) %return, i8 %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result, i8 %value, ptr undef)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %call.result, i32 1025, i1 false)
|
||||
%0 = getelementptr inbounds nuw i8, ptr %return, i32 1025
|
||||
store i8 %value, ptr %0, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeTwoLargeResults(ptr dereferenceable_or_null(2050) %return, i8 %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result, i8 %value, ptr undef)
|
||||
%0 = add i8 %value, 1
|
||||
%call.result1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result1, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result1, i8 %0, ptr undef)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %call.result, i32 1025, i1 false)
|
||||
%1 = getelementptr inbounds nuw i8, ptr %return, i32 1025
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %1, ptr noundef nonnull align 1 dereferenceable(1025) %call.result1, i32 1025, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeMixedLargeResults(ptr dereferenceable_or_null(2051) %return, i8 %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result, i8 %value, ptr undef)
|
||||
%0 = add i8 %value, 1
|
||||
%1 = add i8 %value, 2
|
||||
%call.result1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result1, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result1, i8 %1, ptr undef)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %call.result, i32 1025, i1 false)
|
||||
%2 = getelementptr inbounds nuw i8, ptr %return, i32 1025
|
||||
store i8 %0, ptr %2, align 1
|
||||
%3 = getelementptr inbounds nuw i8, ptr %return, i32 1026
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %3, ptr noundef nonnull align 1 dereferenceable(1025) %call.result1, i32 1025, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chooseLargeValue(ptr dereferenceable_or_null(1025) %return, i1 %flag, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result, i8 1, ptr undef)
|
||||
br i1 %flag, label %if.then, label %if.done
|
||||
|
||||
if.then: ; preds = %entry
|
||||
%call.result1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %call.result1, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @main.makeLargeValue(ptr nonnull %call.result1, i8 42, ptr undef)
|
||||
br label %if.done
|
||||
|
||||
if.done: ; preds = %if.then, %entry
|
||||
%0 = phi ptr [ %call.result, %entry ], [ %call.result1, %if.then ]
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %0, i32 1025, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makePointerLargeValue(ptr dereferenceable_or_null(1032) %return, ptr dereferenceable_or_null(1) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%complit = call align 4 dereferenceable(1032) ptr @runtime.alloc(i32 1032, ptr nonnull @"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002", ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #9
|
||||
br i1 false, label %store.throw, label %store.next
|
||||
|
||||
store.next: ; preds = %entry
|
||||
%0 = getelementptr inbounds nuw i8, ptr %complit, i32 1028
|
||||
store ptr %value, ptr %0, align 4
|
||||
%1 = call align 4 dereferenceable(1032) ptr @runtime.alloc(i32 1032, ptr nonnull @"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002", ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %1, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 4 dereferenceable(1032) %1, ptr noundef nonnull align 4 dereferenceable(1032) %complit, i32 1032, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1032) %return, ptr noundef nonnull align 4 dereferenceable(1032) %1, i32 1032, i1 false)
|
||||
ret void
|
||||
|
||||
store.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.assertLargeValue(ptr %value.typecode, ptr %value.value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%large = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %large, ptr nonnull %stackalloc, ptr undef) #9
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %value.typecode, ptr nonnull @"reflect/types.typeid:named:main.largeValue", ptr undef) #9
|
||||
%typeassert.result = call align 1 dereferenceable(1026) ptr @runtime.alloc(i32 1026, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %typeassert.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memset.p0.i32(ptr noundef nonnull align 1 dereferenceable(1026) %typeassert.result, i8 0, i32 1026, i1 false)
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
%0 = getelementptr inbounds nuw i8, ptr %typeassert.result, i32 1025
|
||||
store i1 %typecode, ptr %0, align 1
|
||||
%t2 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %t2, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %t2, ptr noundef nonnull align 1 dereferenceable(1025) %typeassert.result, i32 1025, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %large, ptr noundef nonnull align 1 dereferenceable(1025) %t2, i32 1025, i1 false)
|
||||
br i1 %typecode, label %if.done, label %if.then
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %typeassert.result, ptr noundef nonnull align 1 dereferenceable(1025) %value.value, i32 1025, i1 false)
|
||||
br label %typeassert.next
|
||||
|
||||
if.done: ; preds = %typeassert.next
|
||||
%1 = getelementptr inbounds nuw i8, ptr %large, i32 1024
|
||||
%2 = load i8, ptr %1, align 1
|
||||
ret i8 %2
|
||||
|
||||
if.then: ; preds = %typeassert.next
|
||||
ret i8 0
|
||||
}
|
||||
|
||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
||||
|
||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
|
||||
declare void @llvm.memset.p0.i32(ptr writeonly captures(none), i8, i32, i1 immarg) #7
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.useLargeMap(ptr readonly dereferenceable_or_null(1025) %key, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call ptr @runtime.hashmapMakeGeneric(i32 1025, i32 1025, i32 1, ptr null, ptr nonnull @runtime.hash32, ptr null, ptr nonnull @runtime.memequal, ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @runtime.hashmapBinarySet(ptr %0, ptr %key, ptr %value, ptr undef) #9
|
||||
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
%hashmap.result = call align 1 dereferenceable(1026) ptr @runtime.alloc(i32 1026, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %hashmap.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
%1 = call i1 @runtime.hashmapBinaryGet(ptr %0, ptr %key, ptr nonnull %hashmap.result, i32 1025, ptr undef) #9
|
||||
%2 = getelementptr inbounds nuw i8, ptr %hashmap.result, i32 1025
|
||||
store i1 %1, ptr %2, align 1
|
||||
%t3 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %t3, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %t3, ptr noundef nonnull align 1 dereferenceable(1025) %hashmap.result, i32 1025, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %result, ptr noundef nonnull align 1 dereferenceable(1025) %t3, i32 1025, i1 false)
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.result, i32 1025
|
||||
%t4 = load i1, ptr %3, align 1
|
||||
br i1 %t4, label %if.done, label %if.then
|
||||
|
||||
if.done: ; preds = %entry
|
||||
%4 = getelementptr inbounds nuw i8, ptr %result, i32 1024
|
||||
%5 = load i8, ptr %4, align 1
|
||||
ret i8 %5
|
||||
|
||||
if.then: ; preds = %entry
|
||||
ret i8 0
|
||||
}
|
||||
|
||||
declare i32 @runtime.hash32(ptr, i32, i32, ptr) #0
|
||||
|
||||
declare i1 @runtime.memequal(ptr, ptr, i32, ptr) #0
|
||||
|
||||
declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(48), ptr, ptr, ptr) #0
|
||||
|
||||
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(48), ptr, ptr, i32, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.useLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.op1 = alloca %runtime.channelOp, align 8
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||
call void @runtime.chanSend(ptr %ch, ptr %value, ptr nonnull %chan.op, ptr undef) #9
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
%chan.result = call align 1 dereferenceable(1026) ptr @runtime.alloc(i32 1026, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %chan.result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op1)
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.result, ptr nonnull %chan.op1, ptr undef) #9
|
||||
%1 = getelementptr inbounds nuw i8, ptr %chan.result, i32 1025
|
||||
store i1 %0, ptr %1, align 1
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op1)
|
||||
%t2 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %t2, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %t2, ptr noundef nonnull align 1 dereferenceable(1025) %chan.result, i32 1025, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %result, ptr noundef nonnull align 1 dereferenceable(1025) %t2, i32 1025, i1 false)
|
||||
%2 = getelementptr inbounds nuw i8, ptr %chan.result, i32 1025
|
||||
%t3 = load i1, ptr %2, align 1
|
||||
br i1 %t3, label %if.done, label %if.then
|
||||
|
||||
if.done: ; preds = %entry
|
||||
%3 = getelementptr inbounds nuw i8, ptr %result, i32 1024
|
||||
%4 = load i8, ptr %3, align 1
|
||||
ret i8 %4
|
||||
|
||||
if.then: ; preds = %entry
|
||||
ret i8 0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.start.p0(ptr captures(none)) #8
|
||||
|
||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.end.p0(ptr captures(none)) #8
|
||||
|
||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.selectLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%select.block.alloca = alloca [2 x %runtime.channelOp], align 8
|
||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||
%select.recvbuf.alloca = alloca [1025 x i8], align 1
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %select.recvbuf.alloca)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %select.states.alloca)
|
||||
store ptr %ch, ptr %select.states.alloca, align 4
|
||||
%select.states.alloca.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 4
|
||||
store ptr %value, ptr %select.states.alloca.repack3, align 4
|
||||
%0 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 8
|
||||
store ptr %ch, ptr %0, align 4
|
||||
%.repack5 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
|
||||
store ptr null, ptr %.repack5, align 4
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %select.block.alloca)
|
||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr nonnull %select.recvbuf.alloca, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr nonnull %select.block.alloca, i32 2, i32 2, ptr undef) #9
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %select.block.alloca)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %select.states.alloca)
|
||||
call void @runtime.trackPointer(ptr nonnull %select.recvbuf.alloca, ptr nonnull %stackalloc, ptr undef) #9
|
||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||
%2 = icmp eq i32 %1, 0
|
||||
br i1 %2, label %select.body, label %select.next
|
||||
|
||||
select.body: ; preds = %entry
|
||||
ret i8 0
|
||||
|
||||
select.next: ; preds = %entry
|
||||
%3 = icmp eq i32 %1, 1
|
||||
br i1 %3, label %select.body1, label %select.next2
|
||||
|
||||
select.body1: ; preds = %select.next
|
||||
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
|
||||
%select.received = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull %select.received, ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %select.received, ptr noundef nonnull align 1 dereferenceable(1025) %select.recvbuf.alloca, i32 1025, i1 false)
|
||||
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %result, ptr noundef nonnull align 1 dereferenceable(1025) %select.received, i32 1025, i1 false)
|
||||
%4 = getelementptr inbounds nuw i8, ptr %result, i32 1024
|
||||
%5 = load i8, ptr %4, align 1
|
||||
ret i8 %5
|
||||
|
||||
select.next2: ; preds = %select.next
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @runtime.trackPointer(ptr nonnull @"main$pack", ptr nonnull %stackalloc, ptr undef) #9
|
||||
call void @runtime._panic(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull @"main$pack", ptr undef) #9
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #0
|
||||
|
||||
declare void @runtime._panic(ptr, ptr, ptr) #0
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #3 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-indirect-result"="true" "tinygo-invoke"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}" "tinygo-methods"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}; main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" "tinygo-methods"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}; main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" }
|
||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.readLargeValue" }
|
||||
attributes #7 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||
attributes #8 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #9 = { nounwind }
|
||||
Vendored
+16
-12
@@ -3,44 +3,48 @@ source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
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(ptr %x, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret [0 x i32] zeroinitializer
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
||||
ret ptr %x
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
||||
ret ptr %x
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
||||
ret ptr %x
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
Vendored
-39
@@ -35,12 +35,6 @@ func withLinkageName1() {
|
||||
//go:linkname withLinkageName2 somepkg.someFunction2
|
||||
func withLinkageName2()
|
||||
|
||||
// Import a function from a different package using go:linknamestd (the standard
|
||||
// library variant of go:linkname introduced in Go 1.27).
|
||||
//
|
||||
//go:linknamestd withLinkageNameStd somepkg.someFunctionStd
|
||||
func withLinkageNameStd()
|
||||
|
||||
// Function has an 'inline hint', similar to the inline keyword in C.
|
||||
//
|
||||
//go:inline
|
||||
@@ -121,36 +115,3 @@ func doesNotEscapeParam(a *int, b []int, c chan int, d *[0]byte)
|
||||
//go:noescape
|
||||
func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
|
||||
}
|
||||
|
||||
//go:noheap
|
||||
func doesHeapAlloc() *int {
|
||||
return new(int)
|
||||
}
|
||||
|
||||
// Define a function in a different package using a file-level go:linkname.
|
||||
// (Same as withLinkageName1, but with the //go:linkname directive detached
|
||||
// from the function declaration — see https://github.com/tinygo-org/tinygo/issues/4395)
|
||||
func withFileLevelLinkageName1() {
|
||||
}
|
||||
|
||||
// Import a function from a different package using a file-level go:linkname.
|
||||
// (Same as withLinkageName2, but with the //go:linkname directive detached
|
||||
// from the function declaration.)
|
||||
func withFileLevelLinkageName2()
|
||||
|
||||
//go:linkname withFileLevelLinkageName1 somepkg.someFileLevelFunction1
|
||||
//go:linkname withFileLevelLinkageName2 somepkg.someFileLevelFunction2
|
||||
|
||||
// File-level linkname directives can also appear between two function
|
||||
// declarations, in which case Go's AST attaches them as the doc comment
|
||||
// of the following function — even when the directive's localname refers
|
||||
// to a different function. Exercise that case: the directive below names
|
||||
// withAdjacentLinkageName, but Go will attach it to
|
||||
// sentinelAfterAdjacentLinkname's Doc. The file-level scan must find it
|
||||
// by walking comment groups regardless of which decl they're attached to.
|
||||
func withAdjacentLinkageName() {
|
||||
}
|
||||
|
||||
//go:linkname withAdjacentLinkageName somepkg.someAdjacentFunction
|
||||
func sentinelAfterAdjacentLinkname() {
|
||||
}
|
||||
|
||||
Vendored
+31
-63
@@ -11,127 +11,95 @@ target triple = "wasm32-unknown-wasi"
|
||||
@undefinedGlobalNotInSection = external global i32, align 4
|
||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @extern_func() #2 {
|
||||
define void @extern_func() #3 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @somepkg.someFunction2(ptr) #0
|
||||
|
||||
declare void @somepkg.someFunctionStd(ptr) #0
|
||||
declare void @somepkg.someFunction2(ptr) #1
|
||||
|
||||
; Function Attrs: inlinehint nounwind
|
||||
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 {
|
||||
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #4 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 {
|
||||
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #5 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.useGeneric(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.useGeneric(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @"main.noinlineGenericFunc[basic:int8]"(ptr undef)
|
||||
call void @"main.noinlineGenericFunc[int8]"(ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define linkonce_odr hidden void @"main.noinlineGenericFunc[basic:int8]"(ptr %context) unnamed_addr #4 {
|
||||
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #5 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.functionInSection(ptr %context) unnamed_addr #4 section ".special_function_section" {
|
||||
define hidden void @main.functionInSection(ptr %context) unnamed_addr #5 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define void @exportedFunctionInSection() #5 section ".special_function_section" {
|
||||
define void @exportedFunctionInSection() #6 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.declaredImport() #6
|
||||
declare void @main.declaredImport() #7
|
||||
|
||||
declare void @imported() #7
|
||||
declare void @imported() #8
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @exported() #8 {
|
||||
define void @exported() #9 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.undefinedFunctionNotInSection(ptr) #0
|
||||
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
||||
|
||||
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #0
|
||||
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.doesHeapAlloc(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call align 4 dereferenceable(4) ptr @runtime.alloc_noheap(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #10
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #10
|
||||
ret ptr %new
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc_noheap(i32, ptr, ptr) #9
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @somepkg.someFileLevelFunction1(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @somepkg.someFileLevelFunction2(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @somepkg.someAdjacentFunction(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.sentinelAfterAdjacentLinkname(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
||||
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
|
||||
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||
attributes #8 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
|
||||
attributes #9 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #10 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
||||
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
|
||||
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||
attributes #8 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||
attributes #9 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
|
||||
|
||||
Vendored
+31
-31
@@ -3,28 +3,31 @@ source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret i32 %ints.len
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret i32 %ints.cap
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %ints.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
@@ -39,10 +42,10 @@ lookup.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(ptr) #0
|
||||
declare void @runtime.lookupPanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
||||
@@ -63,13 +66,10 @@ entry:
|
||||
ret { ptr, i32, i32 } %4
|
||||
}
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||
|
||||
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #0
|
||||
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
||||
@@ -84,7 +84,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
%copy.size = shl nuw i32 %copy.n, 2
|
||||
@@ -99,7 +99,7 @@ declare i32 @llvm.umin.i32(i32, i32) #3
|
||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
@@ -118,10 +118,10 @@ slice.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.slicePanic(ptr) #0
|
||||
declare void @runtime.slicePanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
@@ -142,7 +142,7 @@ slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||
@@ -163,7 +163,7 @@ slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||
@@ -184,7 +184,7 @@ slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = getelementptr i8, ptr %p, i32 %len
|
||||
@@ -193,7 +193,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = trunc i64 %len to i32
|
||||
@@ -203,7 +203,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = icmp ult i32 %s.len, 4
|
||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||
@@ -216,10 +216,10 @@ slicetoarray.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.sliceToArrayPointerPanic(ptr) #0
|
||||
declare void @runtime.sliceToArrayPointerPanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 {
|
||||
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
||||
@@ -234,7 +234,7 @@ slicetoarray.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp ugt i32 %len, 1073741823
|
||||
@@ -256,10 +256,10 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp eq ptr %ptr, null
|
||||
@@ -281,7 +281,7 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
@@ -305,7 +305,7 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
@@ -328,9 +328,9 @@ unsafe.Slice.throw: ; preds = %entry
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
Vendored
+25
-21
@@ -7,34 +7,37 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret %runtime._string { ptr @"main$string", i32 3 }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #1 {
|
||||
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret %runtime._string zeroinitializer
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret i32 %s.len
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #1 {
|
||||
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %s.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
@@ -45,40 +48,40 @@ lookup.next: ; preds = %entry
|
||||
ret i8 %1
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(ptr undef) #2
|
||||
call void @runtime.lookupPanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(ptr) #0
|
||||
declare void @runtime.lookupPanic(ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2
|
||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2
|
||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||
%1 = xor i1 %0, true
|
||||
ret i1 %1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #2
|
||||
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #1 {
|
||||
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = zext i8 %x to i32
|
||||
%.not = icmp ugt i32 %s.len, %0
|
||||
@@ -90,10 +93,11 @@ lookup.next: ; preds = %entry
|
||||
ret i8 %2
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(ptr undef) #2
|
||||
call void @runtime.lookupPanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
Vendored
+54
-24
@@ -5,16 +5,19 @@ target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%main.hasPadding = type { i1, i32, i1 }
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
|
||||
entry:
|
||||
%hashmap.key = alloca %main.hasPadding, align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -24,23 +27,29 @@ entry:
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||
%3 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
%4 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #5
|
||||
%5 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #5
|
||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
%4 = load i32, ptr %hashmap.value, align 4
|
||||
%6 = load i32, ptr %hashmap.value, align 4
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret i32 %4
|
||||
ret i32 %6
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4
|
||||
|
||||
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0
|
||||
declare void @runtime.memzero(ptr, i32, ptr) #1
|
||||
|
||||
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
|
||||
entry:
|
||||
%hashmap.key = alloca %main.hasPadding, align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -51,16 +60,20 @@ entry:
|
||||
store i32 5, ptr %hashmap.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
%4 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #5
|
||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #5
|
||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.hashmapGenericSet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, ptr) #0
|
||||
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
|
||||
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
|
||||
entry:
|
||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -71,15 +84,23 @@ entry:
|
||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||
%0 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||
%0 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
||||
%1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #5
|
||||
%2 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 13
|
||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #5
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 21
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
%4 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #5
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
||||
%1 = load i32, ptr %hashmap.value, align 4
|
||||
%5 = load i32, ptr %hashmap.value, align 4
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret i32 %1
|
||||
ret i32 %5
|
||||
}
|
||||
|
||||
; Function Attrs: noinline nounwind
|
||||
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
|
||||
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
|
||||
entry:
|
||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
@@ -91,20 +112,29 @@ entry:
|
||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||
%0 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
||||
%1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #5
|
||||
%2 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 13
|
||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #5
|
||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 21
|
||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #5
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||
define hidden void @main.main(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #4 = { nounwind }
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||
attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
@@ -59,6 +59,7 @@ func TestCorpus(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
repo := repo
|
||||
name := repo.Repo
|
||||
if repo.Tags != "" {
|
||||
name += "(" + strings.ReplaceAll(repo.Tags, " ", "-") + ")"
|
||||
@@ -131,6 +132,7 @@ func TestCorpus(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, dir := range repo.Subdirs {
|
||||
dir := dir
|
||||
t.Run(dir.Pkg, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -116,7 +116,10 @@ func Diff(oldName string, old []byte, newName string, new []byte) []byte {
|
||||
|
||||
// End chunk with common lines for context.
|
||||
if len(ctext) > 0 {
|
||||
n := min(end.x-start.x, C)
|
||||
n := end.x - start.x
|
||||
if n > C {
|
||||
n = C
|
||||
}
|
||||
for _, s := range x[start.x : start.x+n] {
|
||||
ctext = append(ctext, " "+s)
|
||||
count.x++
|
||||
@@ -231,7 +234,7 @@ func tgs(x, y []string) []pair {
|
||||
for i := range T {
|
||||
T[i] = n + 1
|
||||
}
|
||||
for i := range n {
|
||||
for i := 0; i < n; i++ {
|
||||
k := sort.Search(n, func(k int) bool {
|
||||
return T[k] >= J[i]
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user