Compare commits

...

31 Commits

Author SHA1 Message Date
Ayke van Laethem c660750636 tools: process SVD files in parallel
This speeds up the generation of SVD files, for example as part of a
package build (where parallelism may not be desired, or tools should
auto-detect the number of threads to use).
2023-10-04 15:24:09 +02:00
Ayke van Laethem 2320b18953 ci: use matrix instead of duplicating the Linux cross job
This reduces a lot of copy-pasted code (that was in fact different in a
small way!)
2023-10-04 14:21:43 +02:00
Ayke van Laethem 3b1913ac57 all: use the new LLVM pass manager
The old LLVM pass manager is deprecated and should not be used anymore.
Moreover, the pass manager builder (which we used to set up a pass
pipeline) is actually removed from LLVM entirely in LLVM 17:
https://reviews.llvm.org/D145387
https://reviews.llvm.org/D145835

The new pass manager does change the binary size in many cases: both
growing and shrinking it. However, on average the binary size remains
more or less the same.

This is needed as a preparation for LLVM 17.
2023-10-04 13:05:58 +02:00
Ayke van Laethem 1da1abe314 all: remove LLVM 14 support
This is a big change: apart from removing LLVM 14 it also removes typed
pointer support (which was only fully supported in LLVM up to version
14). This removes about 200 lines of code, but more importantly removes
a ton of special cases for LLVM 14.
2023-10-01 18:32:15 +02:00
ayan george c9721197d5 bulid: Rename Makefile to GNUmakefile
Prior to this commit, the build instructions were encoded in Makefiles
that contained GNU extensions that are unique to GNU Make and
incompatible with older implementations.

Thie commit renames all Makefiles to GNUmakefile which clearly denotes
that the file contains GNU extensions.

GNU Make actually first looks for a GNUmakefile, then makefile, THEN
Makefile so in addition to simply being more correct and portable, it
saves a few unnecessary failed attempts to open the correct build
file.
2023-10-01 14:55:34 +02:00
ayan george 738747bd05 build: use $(MAKE) to represent make executable
Prior to this commit, our Makefiles assumed the name of the make program
was simply "make".

Since we use GNU make extensions, this isn't always the case --
operating systems like FreeBSD come with their own implementation named
make which can be incompatible with the extensions used in by tinygo.

To run a compatible make on some of these systems, we have explicitly
call GNU make by running gmake.

This commit changes references to the command "make" to "$(MAKE)" which
is a variable that contains the name of the executable invoked to
process the Makefile.

This allow the Makefiles to be uniformly processed by the same make
program.
2023-10-01 14:55:34 +02:00
ayan george 0bb4a8abeb docs: Update BUILDING.md
Indicate that GNU Make is the version of make required to build.
2023-09-29 17:45:59 +02:00
Ayke van Laethem 6ef8fcd537 cgo: add C._Bool type
This fixes https://github.com/tinygo-org/tinygo/issues/3926.

While working on this I've found another bug: if C.bool is referenced
from within Go, it isn't available anymore on the C side. This is an
existing bug that also applies to float and double, but may be less
likely to be triggered there.
This bug is something to be fixed at a later time (it has something to
do with preprocessor defines).
2023-09-24 07:41:26 -07:00
Ayke van Laethem a896f7f218 transform: fix bug in StringToBytes optimization pass
Previously, this pass would convert any read-only use of a
runtime.stringToBytes call to use the original string buffer instead.
This is incorrect: if there are any writes to the resulting buffer, none
of the slice buffer pointers can be converted to use the original
read-only string buffer.

This commit fixes that bug and adds a test to prove the new (correct)
behavior.
2023-09-23 07:48:43 -07:00
Ayke van Laethem 081d2e58c6 interp: print LLVM instruction in traceback
The old traceback would look like this:

    # internal/godebug
    /usr/local/go/src/internal/godebug/godebug.go:101:11: interp: test
    call <2> 0 <3> 0

    traceback:
    /usr/local/go/src/internal/godebug/godebug.go:101:11:
    call <2> 0 <3> 0
    /usr/local/go/src/internal/godebug:
    call <1> 0

With this patch, it looks like this:

    # io/fs
    /usr/local/go/src/io/fs/fs.go:144:45: interp: test
      %0 = load %runtime._interface, ptr @"internal/oserror.ErrInvalid", align 8, !dbg !316

    traceback:
    /usr/local/go/src/io/fs/fs.go:144:45:
      %0 = load %runtime._interface, ptr @"internal/oserror.ErrInvalid", align 8, !dbg !316
    /usr/local/go/src/io/fs/fs.go:137:28:
      %0 = call %runtime._interface @"io/fs.errInvalid"(ptr undef), !dbg !317

For developers (like me) who are familiar with LLVM, this is probably
easier to read. For users, I'm not sure: the instructions have quite a
lot of distracting fluff in them. But at least it contains the function
names that are called (which are not currently present in the old
traceback).
...that said, having the LLVM instructions in a bug report is probably
going to be easier for people who are familar with LLVM.
2023-09-22 15:28:52 +02:00
Ayke van Laethem ed2a98c7d0 ci: redo LLVM build on Windows
MinGW got updated, so it looks like we need to do a new LLVM build.
2023-09-21 21:33:34 +02:00
Ayke van Laethem 731532cd2b all: release 0.30.0
This is a small release just in time for GopherCon US.

Some of the big changes of this release are:

  - switch to LLVM 16
  - fixes for two separate hard-to-reproduce compiler crashes
  - added support for the Adafruit Gemma M0
2023-09-21 08:03:16 +02:00
Kenneth Bell adadbadec3 interp: improve unknown opcode handling 2023-09-21 01:18:05 +02:00
Kenneth Bell 58fafaeb5c build: #3893 do not return LLVM structs from Build 2023-09-21 01:18:05 +02:00
Dan Kegel 13a8eae0d4 build: build Go SSA serially [issue 3895]
From
https://github.com/tinygo-org/tinygo/pull/3915#issuecomment-1724405109

According to Ayke, it slows down the build but seems to reliably fix https://github.com/tinygo-org/tinygo/issues/3895
2023-09-20 23:22:18 +02:00
Ayke van Laethem 42da7654ec compiler: don't use types in the global context
This usually works by chance, but leads to crashes. So we should never
ever do this.

I'm pretty sure this is the crash behind this issue: https://github.com/tinygo-org/tinygo/issues/3894

It may also have caused this crash: https://github.com/tinygo-org/tinygo/issues/3874

I have a suspicion this is also behind the rather crash-prone CircleCI
jobs, that we haven't been able to find the source of. But we'll find
out soon enough once this fix is merged.

To avoid hitting this issue again in the future, I've created a PR to
remove these dangerous functions altogether from the go-llvm API:
https://github.com/tinygo-org/go-llvm/pull/54
2023-09-19 09:21:51 +02:00
Ayke van Laethem 8698a7e496 all: default to LLVM 16
So that `go install` works on MacOS with Homebrew (and on Linux with an
up-to-date distro).
2023-09-18 21:58:02 +02:00
Ayke van Laethem 1d7543e2bf all: switch to LLVM 16
This commit adds support for LLVM 16 and switches to it by default. That
means three LLVM versions are supported at the same time: LLVM 14, 15,
and 16.

This commit includes work by QuLogic:

  * Part of this work was based on a PR by QuLogic:
    https://github.com/tinygo-org/tinygo/pull/3649
    But I also had parts of this already implemented in an old branch I
    already made for LLVM 16.
  * QuLogic also provided a CGo fix here, which is also incorporated in
    this commit:
    https://github.com/tinygo-org/tinygo/pull/3869

The difference with the original PR by QuLogic is that this commit is
more complete:
  * It switches to LLVM 16 by default.
  * It updates some things to also make it work with a self-built LLVM.
  * It fixes the CGo bug in a slightly different way, and also fixes
    another one not included in the original PR.
  * It does not keep compiler tests passing on older LLVM versions. I
    have found this to be quite burdensome and therefore don't generally
    do this - the smoke tests should hopefully catch most regressions.
2023-09-18 21:58:02 +02:00
deadprogram ff32fbbb4f targets: increase default stack size to 32k for wasi/wasm targets
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-17 14:24:21 +02:00
sago35 3ad2530ee7 atsamd21, atsamd51: add support for USB INTERRUPT OUT 2023-09-16 09:30:05 +02:00
deadprogram c4de195f04 machine/rp2040: always use the USB device enum fix, even in chips that supposedly have the HW fix.
Additionally, correct the amount of time spent waiting after USB reset, based on advice from @sago35

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-12 08:41:58 +02:00
Timothy Rule 75aca0f5ee Board support for Adafruit Gemma M0 (#3903)
machine, targets: Board support for Adafruit Gemma M0
2023-09-11 11:11:26 +02:00
Elliott Sales de Andrade bb9a9beed5 Update golang.org/x/tools to v0.12.0 2023-09-10 23:14:58 +02:00
Elliott Sales de Andrade 4042c1d618 Update tools to 0.9.0
This requires updating test data, due to the change noted in the
previous commit.
2023-09-10 23:14:58 +02:00
Elliott Sales de Andrade bf73516259 compiler: Handle nil array and struct constants
This is necessary for tools 0.9.0, which started lifting those since
https://github.com/golang/tools/commit/71ea8f168c160da91116b4ddbd577a8fc95aa334
2023-09-10 23:14:58 +02:00
deadprogram 43d282174f targets: add GoBadge target as alias for PyBadge :)
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-10 15:52:55 +02:00
Damian Gryski 0042bf62a5 compiler,reflect: add support for [...]T -> []T in reflect 2023-09-10 13:05:18 +02:00
Ayke van Laethem f11731ff35 interp: don't copy unknown values in runtime.sliceCopy
This was bug https://github.com/tinygo-org/tinygo/issues/3890.
See the diff for details. Essentially, it means that `copy` in the
interpreter was copying data that wasn't known yet, or copying data into
a slice that could be read externally after the interp pass.
2023-09-10 11:30:09 +02:00
sago35 e9d8a9bc0b goenv: update to new v0.30.0 development version 2023-09-08 17:41:31 +02:00
deadprogram 9d6eb1ff06 machine/usb/adc/midi: improve implementation to include several new messages
such as program changes and pitch bend. Also add error handling for invalid
parameter values such as MIDI channel. This however makes a somewhat breaking
change to the current implementation, in that we now use the typical MIDI user
system of counting MIDI channels from 1-16 instead of from 0-15 as the lower
level USB-MIDI API itself expects.

Also add constant values for continuous controller messages, rename SendCC
function, and add SysEx function.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-07 08:41:57 +02:00
Ayke van Laethem 4643401a1d runtime: refactor markGlobals to findGlobals
Instead of markGlobals calling markRoots unconditionally (which doesn't
make sense for -gc=none and -gc=leaking), provide markRoots as a
callback function.

This is in preparation for -gc=boehm, where the previous design is even
more awkward and a callback makes far more sense.

I've tested the size impact using `make smoketest XTENSA=0`. There is
none, except for two cases:

  * One with `-opt=0` so const-propagation for the callback didn't take
    place.
  * One other on AVR, I don't know why but as it's only 16 bytes in a
    very specific case I'm going to assume it's just a random change in
    compiler output that caused a size difference.
2023-09-05 00:42:01 +02:00
115 changed files with 1328 additions and 1078 deletions
+6 -6
View File
@@ -10,12 +10,12 @@ commands:
steps: steps:
- restore_cache: - restore_cache:
keys: keys:
- llvm-source-14-v3 - llvm-source-16-v3
- run: - run:
name: "Fetch LLVM source" name: "Fetch LLVM source"
command: make llvm-source command: make llvm-source
- save_cache: - save_cache:
key: llvm-source-14-v3 key: llvm-source-16-v3
paths: paths:
- llvm-project/clang/lib/Headers - llvm-project/clang/lib/Headers
- llvm-project/clang/include - llvm-project/clang/include
@@ -89,7 +89,7 @@ commands:
# formatting of generated files. # formatting of generated files.
name: Check Go code formatting name: Check Go code formatting
command: make fmt-check command: make fmt-check
- run: make gen-device -j4 - run: make gen-device
- run: make smoketest XTENSA=0 - run: make smoketest XTENSA=0
- save_cache: - save_cache:
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }} key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
@@ -98,12 +98,12 @@ commands:
- /go/pkg/mod - /go/pkg/mod
jobs: jobs:
test-llvm14-go118: test-llvm15-go118:
docker: docker:
- image: golang:1.18-buster - image: golang:1.18-buster
steps: steps:
- test-linux: - test-linux:
llvm: "14" llvm: "15"
resource_class: large resource_class: large
workflows: workflows:
@@ -111,4 +111,4 @@ workflows:
jobs: jobs:
# This tests our lowest supported versions of Go and LLVM, to make sure at # This tests our lowest supported versions of Go and LLVM, to make sure at
# least the smoke tests still pass. # least the smoke tests still pass.
- test-llvm14-go118 - test-llvm15-go118
+4 -4
View File
@@ -33,7 +33,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-macos-v3 key: llvm-source-16-macos-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -58,7 +58,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-macos-v4 key: llvm-build-16-macos-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -88,7 +88,7 @@ jobs:
if: steps.cache-wasi-libc.outputs.cache-hit != 'true' if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc run: make wasi-libc
- name: make gen-device - name: make gen-device
run: make -j3 gen-device run: make gen-device
- name: Test TinyGo - name: Test TinyGo
shell: bash shell: bash
run: make test GOTESTFLAGS="-short" run: make test GOTESTFLAGS="-short"
@@ -120,7 +120,7 @@ jobs:
- name: Install LLVM - name: Install LLVM
shell: bash shell: bash
run: | run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15 HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install Go - name: Install Go
+1 -1
View File
@@ -53,7 +53,7 @@ jobs:
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-15 build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-16
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
- name: Trigger Drivers repo build on Github Actions - name: Trigger Drivers repo build on Github Actions
+31 -135
View File
@@ -43,7 +43,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-alpine-v3 key: llvm-source-16-linux-alpine-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -68,7 +68,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-alpine-v4 key: llvm-build-16-linux-alpine-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -194,7 +194,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-asserts-v3 key: llvm-source-16-linux-asserts-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -219,7 +219,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-asserts-v4 key: llvm-build-16-linux-asserts-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -255,7 +255,7 @@ jobs:
- name: Build wasi-libc - name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true' if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc run: make wasi-libc
- run: make gen-device -j4 - run: make gen-device
- name: Test TinyGo - name: Test TinyGo
run: make ASSERT=1 test run: make ASSERT=1 test
- name: Build TinyGo - name: Build TinyGo
@@ -267,7 +267,7 @@ jobs:
- run: make smoketest - run: make smoketest
- run: make wasmtest - run: make wasmtest
- run: make tinygo-baremetal - run: make tinygo-baremetal
build-linux-arm: build-linux-cross:
# Build ARM Linux binaries, ready for release. # Build ARM Linux binaries, ready for release.
# This intentionally uses an older Linux image, so that we compile against # This intentionally uses an older Linux image, so that we compile against
# an older glibc version and therefore are compatible with a wide range of # an older glibc version and therefore are compatible with a wide range of
@@ -276,6 +276,16 @@ jobs:
# in that process to avoid doing lots of duplicate work and to avoid # in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as # complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball. # part of the release tarball.
strategy:
matrix:
goarch: [ arm, arm64 ]
include:
- goarch: arm64
toolchain: aarch64-linux-gnu
libc: arm64
- goarch: arm
toolchain: arm-linux-gnueabihf
libc: armhf
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: build-linux needs: build-linux
steps: steps:
@@ -286,8 +296,8 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install --no-install-recommends \ sudo apt-get install --no-install-recommends \
qemu-user \ qemu-user \
g++-arm-linux-gnueabihf \ g++-${{ matrix.toolchain }} \
libc6-dev-armhf-cross libc6-dev-${{ matrix.libc }}-cross
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
@@ -297,7 +307,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-v3 key: llvm-source-16-linux-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -322,7 +332,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-arm-v4 key: llvm-build-16-linux-${{ matrix.goarch }}-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -333,7 +343,7 @@ jobs:
# Install build dependencies. # Install build dependencies.
sudo apt-get install --no-install-recommends ninja-build sudo apt-get install --no-install-recommends ninja-build
# build! # build!
make llvm-build CROSS=arm-linux-gnueabihf make llvm-build CROSS=${{ matrix.toolchain }}
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache - name: Save LLVM build cache
@@ -346,14 +356,14 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-binaryen id: cache-binaryen
with: with:
key: binaryen-linux-arm-v1 key: binaryen-linux-${{ matrix.goarch }}-v1
path: build/wasm-opt path: build/wasm-opt
- name: Build Binaryen - name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true' if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: | run: |
sudo apt-get install --no-install-recommends ninja-build sudo apt-get install --no-install-recommends ninja-build
git submodule update --init lib/binaryen git submodule update --init lib/binaryen
make CROSS=arm-linux-gnueabihf binaryen make CROSS=${{ matrix.toolchain }} binaryen
- name: Install fpm - name: Install fpm
run: | run: |
sudo gem install --version 4.0.7 public_suffix sudo gem install --version 4.0.7 public_suffix
@@ -361,7 +371,7 @@ jobs:
sudo gem install --no-document fpm sudo gem install --no-document fpm
- name: Build TinyGo binary - name: Build TinyGo binary
run: | run: |
make CROSS=arm-linux-gnueabihf make CROSS=${{ matrix.toolchain }}
- name: Download amd64 release - name: Download amd64 release
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
@@ -374,129 +384,15 @@ jobs:
run: | run: |
cp -p build/tinygo build/release/tinygo/bin cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin cp -p build/wasm-opt build/release/tinygo/bin
- name: Create arm release - name: Create ${{ matrix.goarch }} release
run: | run: |
make release deb RELEASEONLY=1 DEB_ARCH=armhf make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz cp -p build/release.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
cp -p build/release.deb /tmp/tinygo_armhf.deb cp -p build/release.deb /tmp/tinygo_${{ matrix.libc }}.deb
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: linux-arm-double-zipped name: linux-${{ matrix.goarch }}-double-zipped
path: | path: |
/tmp/tinygo.linux-arm.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
/tmp/tinygo_armhf.deb /tmp/tinygo_${{ matrix.libc }}.deb
build-linux-arm64:
# Build ARM64 Linux binaries, ready for release.
# It is set to "needs: build-linux" because it modifies the release created
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
runs-on: ubuntu-20.04
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install apt dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
qemu-user \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
ninja-build
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.21'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-15-linux-v3
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-15-linux-arm64-v4
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# build!
make llvm-build CROSS=aarch64-linux-gnu
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v3
id: cache-binaryen
with:
key: binaryen-linux-arm64-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
git submodule update --init lib/binaryen
make CROSS=aarch64-linux-gnu binaryen
- name: Install fpm
run: |
sudo gem install --version 4.0.7 public_suffix
sudo gem install --version 2.7.6 dotenv
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=aarch64-linux-gnu
- name: Download amd64 release
uses: actions/download-artifact@v3
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
run: |
mkdir -p build/release
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release
run: |
cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin
- name: Create arm64 release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=arm64
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
cp -p build/release.deb /tmp/tinygo_arm64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v3
with:
name: linux-arm64-double-zipped
path: |
/tmp/tinygo.linux-arm64.tar.gz
/tmp/tinygo_arm64.deb
+2 -2
View File
@@ -35,8 +35,8 @@ jobs:
uses: docker/metadata-action@v4 uses: docker/metadata-action@v4
with: with:
images: | images: |
tinygo/llvm-15 tinygo/llvm-16
ghcr.io/${{ github.repository_owner }}/llvm-15 ghcr.io/${{ github.repository_owner }}/llvm-16
tags: | tags: |
type=sha,format=long type=sha,format=long
type=raw,value=latest type=raw,value=latest
+7 -7
View File
@@ -24,19 +24,19 @@ jobs:
submodules: true submodules: true
- name: Install apt dependencies - name: Install apt dependencies
run: | run: |
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main' | sudo tee /etc/apt/sources.list.d/llvm.list echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update sudo apt-get update
sudo apt-get install --no-install-recommends -y \ sudo apt-get install --no-install-recommends -y \
llvm-15-dev \ llvm-16-dev \
clang-15 \ clang-16 \
libclang-15-dev \ libclang-16-dev \
lld-15 lld-16
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-sizediff-v1 key: llvm-source-16-sizediff-v1
path: | path: |
llvm-project/compiler-rt llvm-project/compiler-rt
- name: Download LLVM source - name: Download LLVM source
@@ -49,7 +49,7 @@ jobs:
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
- run: make gen-device -j4 - run: make gen-device
- name: Download drivers repo - name: Download drivers repo
run: git clone https://github.com/tinygo-org/drivers.git run: git clone https://github.com/tinygo-org/drivers.git
- name: Save HEAD - name: Save HEAD
+3 -3
View File
@@ -41,7 +41,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-windows-v4 key: llvm-source-16-windows-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -66,7 +66,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-windows-v6 key: llvm-build-16-windows-v2
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -98,7 +98,7 @@ jobs:
run: | run: |
scoop install wasmtime scoop install wasmtime
- name: make gen-device - name: make gen-device
run: make -j3 gen-device run: make gen-device
- name: Test TinyGo - name: Test TinyGo
shell: bash shell: bash
run: make test GOTESTFLAGS="-short" run: make test GOTESTFLAGS="-short"
+1
View File
@@ -19,6 +19,7 @@ LLVM, Clang and LLD are quite light on dependencies, requiring only standard
build tools to be built. Go is of course necessary to build TinyGo itself. build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.18+) * Go (1.18+)
* GNU Make
* Standard build tools (gcc/clang) * Standard build tools (gcc/clang)
* git * git
* CMake * CMake
+22
View File
@@ -1,3 +1,25 @@
0.30.0
---
* **general**
- add LLVM 16 support, use it by default
* **compiler**
- `build`: work around a race condition by building Go SSA serially
- `compiler`: fix a crash by not using the LLVM global context types
- `interp`: don't copy unknown values in `runtime.sliceCopy` to fix miscompile
- `interp`: fix crash in error report by not returning raw LLVM values
* **standard library**
- `machine/usb/adc/midi`: various improvements and API changes
- `reflect`: add support for `[...]T``[]T` in reflect
* **targets**
- `atsamd21`, `atsamd51`: add support for USB INTERRUPT OUT
- `rp2040`: always use the USB device enumeration fix, even in chips that supposedly have the HW fix
- `wasm`: increase default stack size to 32k for wasi/wasm
* **boards**
- `gobadge`: add GoBadge target as alias for PyBadge :)
- `gemma-m0`: add support for the Adafruit Gemma M0
0.29.0 0.29.0
--- ---
+9 -7
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools. # Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order. # Versions are listed here in descending priority order.
LLVM_VERSIONS = 15 14 13 12 11 LLVM_VERSIONS = 16 15
errifempty = $(if $(1),$(1),$(error $(2))) errifempty = $(if $(1),$(1),$(error $(2)))
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2))) detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
toolSearchPathsVersion = $(1)-$(2) toolSearchPathsVersion = $(1)-$(2)
@@ -113,7 +113,7 @@ endif
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp .PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
EXE = .exe EXE = .exe
@@ -154,7 +154,7 @@ LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP) LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
# Other libraries that are needed to link TinyGo. # Other libraries that are needed to link TinyGo.
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMRISCVTargetMCA LLVMX86TargetMCA
# All libraries to be built and linked with the tinygo binary (lib/lib*.a). # 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) LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
@@ -170,7 +170,7 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
# For static linking. # For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","") 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_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++14 CGO_CXXFLAGS=-std=c++17
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) 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 endif
@@ -238,7 +238,7 @@ gen-device-renesas: build/gen-device-svd
# Get LLVM sources. # Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR) git clone -b xtensa_release_16.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM. # Configure LLVM.
@@ -265,7 +265,7 @@ endif
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi @if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
cd lib/wasi-libc && make -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM) cd lib/wasi-libc && $(MAKE) -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
# Check for Node.js used during WASM tests. # Check for Node.js used during WASM tests.
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-))) NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
@@ -280,7 +280,7 @@ endif
# Build the Go compiler. # Build the Go compiler.
tinygo: tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi @if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" . CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc check-nodejs-version test: wasi-libc check-nodejs-version
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform . CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
@@ -558,6 +558,8 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=trinket-m0 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=trinket-m0 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=gemma-m0 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1 $(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1 $(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
+15 -14
View File
@@ -83,8 +83,7 @@ type packageAction struct {
FileHashes map[string]string // hash of every file that's part of the package FileHashes map[string]string // hash of every file that's part of the package
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
Imports map[string]string // map from imported package to action ID hash Imports map[string]string // map from imported package to action ID hash
OptLevel int // LLVM optimization level (0-3) OptLevel string // LLVM optimization level (O0, O1, O2, Os, Oz)
SizeLevel int // LLVM optimization for size level (0-2)
UndefinedGlobals []string // globals that are left as external globals (no initializer) UndefinedGlobals []string // globals that are left as external globals (no initializer)
} }
@@ -158,7 +157,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc) return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
} }
optLevel, sizeLevel, _ := config.OptLevels() optLevel, speedLevel, sizeLevel := config.OptLevel()
compilerConfig := &compiler.Config{ compilerConfig := &compiler.Config{
Triple: config.Triple(), Triple: config.Triple(),
CPU: config.CPU(), CPU: config.CPU(),
@@ -321,7 +320,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
EmbeddedFiles: make(map[string]string, len(allFiles)), EmbeddedFiles: make(map[string]string, len(allFiles)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())), Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel, OptLevel: optLevel,
SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals, UndefinedGlobals: undefinedGlobals,
} }
for filePath, hash := range pkg.FileHashes { for filePath, hash := range pkg.FileHashes {
@@ -344,6 +342,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
// Build the SSA for the given package.
ssaPkg := program.Package(pkg.Pkg)
ssaPkg.Build()
// Now create the job to actually build the package. It will exit early // Now create the job to actually build the package. It will exit early
// if the package is already compiled. // if the package is already compiled.
job := &compileJob{ job := &compileJob{
@@ -528,13 +530,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
irbuilder := mod.Context().NewBuilder() irbuilder := mod.Context().NewBuilder()
defer irbuilder.Dispose() defer irbuilder.Dispose()
irbuilder.SetInsertPointAtEnd(block) irbuilder.SetInsertPointAtEnd(block)
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init") pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
if pkgInit.IsNil() { if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path()) panic("init not found for " + pkg.Pkg.Path())
} }
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "") irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(ptrType)}, "")
} }
irbuilder.CreateRetVoid() irbuilder.CreateRetVoid()
@@ -739,17 +741,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if config.GOOS() == "windows" { if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker. // Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags, ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel), "-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto")) "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" { } else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker. // Options for the ld64-compatible lld linker.
ldflags = append(ldflags, ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel), "--lto-O"+strconv.Itoa(speedLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto")) "-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else { } else {
// Options for the ELF linker. // Options for the ELF linker.
ldflags = append(ldflags, ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel), "--lto-O"+strconv.Itoa(speedLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"), "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
) )
} }
@@ -760,7 +762,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if sizeLevel >= 2 { if sizeLevel >= 2 {
// Workaround with roughly the same effect as // Workaround with roughly the same effect as
// https://reviews.llvm.org/D119342. // https://reviews.llvm.org/D119342.
// Can hopefully be removed in LLVM 15. // Can hopefully be removed in LLVM 18.
ldflags = append(ldflags, ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0") "-mllvm", "--rotation-max-header-size=0")
} }
@@ -1062,10 +1064,9 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return err return err
} }
// Optimization levels here are roughly the same as Clang, but probably not // Run most of the whole-program optimizations (including the whole
// exactly. // O0/O1/O2/Os/Oz optimization pipeline).
optLevel, sizeLevel, inlinerThreshold := config.OptLevels() errs := transform.Optimize(mod, config)
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
if len(errs) > 0 { if len(errs) > 0 {
return newMultiError(errs) return newMultiError(errs)
} }
+19 -2
View File
@@ -56,6 +56,7 @@
#include "llvm/Support/Timer.h" #include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include <memory> #include <memory>
#include <optional>
#include <system_error> #include <system_error>
using namespace clang; using namespace clang;
using namespace clang::driver; using namespace clang::driver;
@@ -103,6 +104,14 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple)) if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue()); Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
VersionTuple Version;
if (Version.tryParse(A->getValue()))
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
else
Opts.DarwinTargetVariantSDKVersion = Version;
}
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu)); Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
Opts.Features = Args.getAllArgValues(OPT_target_feature); Opts.Features = Args.getAllArgValues(OPT_target_feature);
@@ -122,11 +131,12 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.CompressDebugSections = Opts.CompressDebugSections =
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue()) llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
.Case("none", llvm::DebugCompressionType::None) .Case("none", llvm::DebugCompressionType::None)
.Case("zlib", llvm::DebugCompressionType::Z) .Case("zlib", llvm::DebugCompressionType::Zlib)
.Case("zstd", llvm::DebugCompressionType::Zstd)
.Default(llvm::DebugCompressionType::None); .Default(llvm::DebugCompressionType::None);
} }
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations); Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32)) if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64); Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags); Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
@@ -189,6 +199,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings); Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn); Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
Opts.NoTypeCheck = Args.hasArg(OPT_mno_type_check);
Opts.RelocationModel = Opts.RelocationModel =
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic")); std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi)); Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
@@ -214,6 +225,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
.Case("default", EmitDwarfUnwindType::Default); .Case("default", EmitDwarfUnwindType::Default);
} }
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
return Success; return Success;
} }
@@ -265,6 +278,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MCTargetOptions MCOptions; MCTargetOptions MCOptions;
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind; MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
std::unique_ptr<MCAsmInfo> MAI( std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions)); TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
@@ -314,6 +328,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
TheTarget->createMCObjectFileInfo(Ctx, PIC)); TheTarget->createMCObjectFileInfo(Ctx, PIC));
if (Opts.DarwinTargetVariantTriple) if (Opts.DarwinTargetVariantTriple)
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple); MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
if (!Opts.DarwinTargetVariantSDKVersion.empty())
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
Ctx.setObjectFileInfo(MOFI.get()); Ctx.setObjectFileInfo(MOFI.get());
if (Opts.SaveTemporaryLabels) if (Opts.SaveTemporaryLabels)
@@ -353,6 +369,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MCOptions.MCNoWarn = Opts.NoWarn; MCOptions.MCNoWarn = Opts.NoWarn;
MCOptions.MCFatalWarnings = Opts.FatalWarnings; MCOptions.MCFatalWarnings = Opts.FatalWarnings;
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
MCOptions.ABIName = Opts.TargetABI; MCOptions.ABIName = Opts.TargetABI;
// FIXME: There is a bit of code duplication with addPassesToEmitFile. // FIXME: There is a bit of code duplication with addPassesToEmitFile.
+10 -1
View File
@@ -82,6 +82,7 @@ struct AssemblerInvocation {
unsigned NoExecStack : 1; unsigned NoExecStack : 1;
unsigned FatalWarnings : 1; unsigned FatalWarnings : 1;
unsigned NoWarn : 1; unsigned NoWarn : 1;
unsigned NoTypeCheck : 1;
unsigned IncrementalLinkerCompatible : 1; unsigned IncrementalLinkerCompatible : 1;
unsigned EmbedBitcode : 1; unsigned EmbedBitcode : 1;
@@ -97,7 +98,14 @@ struct AssemblerInvocation {
/// Darwin target variant triple, the variant of the deployment target /// Darwin target variant triple, the variant of the deployment target
/// for which the code is being compiled. /// for which the code is being compiled.
llvm::Optional<llvm::Triple> DarwinTargetVariantTriple; std::optional<llvm::Triple> DarwinTargetVariantTriple;
/// The version of the darwin target variant SDK which was used during the
/// compilation
llvm::VersionTuple DarwinTargetVariantSDKVersion;
/// The name of a file to use with \c .secure_log_unique directives.
std::string AsSecureLogFile;
/// @} /// @}
public: public:
@@ -114,6 +122,7 @@ public:
NoExecStack = 0; NoExecStack = 0;
FatalWarnings = 0; FatalWarnings = 0;
NoWarn = 0; NoWarn = 0;
NoTypeCheck = 0;
IncrementalLinkerCompatible = 0; IncrementalLinkerCompatible = 0;
Dwarf64 = 0; Dwarf64 = 0;
DwarfVersion = 0; DwarfVersion = 0;
-6
View File
@@ -178,12 +178,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if strings.HasPrefix(target, "riscv64-") { if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc") args = append(args, "-march=rv64gc")
} }
if strings.HasPrefix(target, "xtensa") {
// Hack to work around an issue in the Xtensa port:
// https://github.com/espressif/llvm-project/issues/52
// Hopefully this will be fixed soon (LLVM 14).
args = append(args, "-D__ELF__")
}
var once sync.Once var once sync.Once
+1 -7
View File
@@ -6,12 +6,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
) )
var Musl = Library{ var Musl = Library{
@@ -93,6 +91,7 @@ var Musl = Library{
"-Wno-string-plus-int", "-Wno-string-plus-int",
"-Wno-ignored-pragmas", "-Wno-ignored-pragmas",
"-Wno-tautological-constant-out-of-range-compare", "-Wno-tautological-constant-out-of-range-compare",
"-Wno-deprecated-non-prototype",
"-Qunused-arguments", "-Qunused-arguments",
// Select include dirs. Don't include standard library includes // Select include dirs. Don't include standard library includes
// (that would introduce host dependencies and other complications), // (that would introduce host dependencies and other complications),
@@ -106,11 +105,6 @@ var Musl = Library{
"-I" + muslDir + "/include", "-I" + muslDir + "/include",
"-fno-stack-protector", "-fno-stack-protector",
} }
llvmMajor, _ := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if llvmMajor >= 15 {
// This flag was added in Clang 15. It is not present in LLVM 14.
cflags = append(cflags, "-Wno-deprecated-non-prototype")
}
return cflags return cflags
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
+2 -2
View File
@@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test. // This is a small number of very diverse targets that we want to test.
tests := []sizeTest{ tests := []sizeTest{
// microcontrollers // microcontrollers
{"hifive1b", "examples/echo", 4612, 280, 0, 2252}, {"hifive1b", "examples/echo", 4484, 280, 0, 2252},
{"microbit", "examples/serial", 2724, 388, 8, 2256}, {"microbit", "examples/serial", 2724, 388, 8, 2256},
{"wioterminal", "examples/pininterrupt", 6039, 1485, 116, 6816}, {"wioterminal", "examples/pininterrupt", 6000, 1484, 116, 6816},
// TODO: also check wasm. Right now this is difficult, because // TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the // wasm binaries are run through wasm-opt and therefore the
+4 -2
View File
@@ -88,6 +88,7 @@ var cgoAliases = map[string]string{
"C.uintptr_t": "uintptr", "C.uintptr_t": "uintptr",
"C.float": "float32", "C.float": "float32",
"C.double": "float64", "C.double": "float64",
"C._Bool": "bool",
} }
// builtinAliases are handled specially because they only exist on the Go side // builtinAliases are handled specially because they only exist on the Go side
@@ -311,10 +312,11 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// Process CGo imports for each file. // Process CGo imports for each file.
for i, f := range files { for i, f := range files {
cf := p.newCGoFile(f, i) cf := p.newCGoFile(f, i)
// Float and double are aliased, meaning that C.float is the same thing // These types are aliased with the corresponding types in C. For
// as float32 in Go. // example, float in C is always float32 in Go.
cf.names["float"] = clangCursor{} cf.names["float"] = clangCursor{}
cf.names["double"] = clangCursor{} cf.names["double"] = clangCursor{}
cf.names["_Bool"] = clangCursor{}
// Now read all the names (identifies) that C defines in the header // Now read all the names (identifies) that C defines in the header
// snippet. // snippet.
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) { cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
+16 -2
View File
@@ -60,6 +60,7 @@ CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c); CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c); long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c); CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c);
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c); unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
@@ -653,8 +654,19 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr { func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
// Strip typedefs, if any. // Strip typedefs, if any.
underlyingType := typ underlyingType := typ
if underlyingType.kind == C.CXType_Elaborated {
// Starting with LLVM 16, the elaborated type is used for more types.
// According to the Clang documentation, the elaborated type has no
// semantic meaning so can be stripped (it is used to better convey type
// name information).
// Source:
// https://clang.llvm.org/doxygen/classclang_1_1ElaboratedType.html#details
// > The type itself is always "sugar", used to express what was written
// > in the source code but containing no additional semantic information.
underlyingType = C.clang_Type_getNamedType(underlyingType)
}
if underlyingType.kind == C.CXType_Typedef { if underlyingType.kind == C.CXType_Typedef {
c := C.tinygo_clang_getTypeDeclaration(typ) c := C.tinygo_clang_getTypeDeclaration(underlyingType)
underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c) underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c)
// TODO: support a chain of typedefs. At the moment, it seems to get // TODO: support a chain of typedefs. At the moment, it seems to get
// stuck in an endless loop when trying to get to the most underlying // stuck in an endless loop when trying to get to the most underlying
@@ -788,6 +800,8 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
return f.makeASTType(underlying, pos) return f.makeASTType(underlying, pos)
case C.CXType_Enum: case C.CXType_Enum:
return f.makeASTType(underlying, pos) return f.makeASTType(underlying, pos)
case C.CXType_Typedef:
return f.makeASTType(underlying, pos)
default: default:
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind)) typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling)) f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
@@ -806,7 +820,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
// makeASTRecordType will create an appropriate error. // makeASTRecordType will create an appropriate error.
cgoRecordPrefix = "record_" cgoRecordPrefix = "record_"
} }
if name == "" { if name == "" || C.tinygo_clang_Cursor_isAnonymous(cursor) != 0 {
// Anonymous record, probably inside a typedef. // Anonymous record, probably inside a typedef.
location := f.getUniqueLocationID(pos, cursor) location := f.getUniqueLocationID(pos, cursor)
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location) name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
-15
View File
@@ -1,15 +0,0 @@
//go:build !byollvm && llvm14
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
*/
import "C"
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !byollvm && !llvm14 //go:build !byollvm && llvm15
package cgo package cgo
+21
View File
@@ -0,0 +1,21 @@
//go:build !byollvm && !llvm15
package cgo
// As of 2023-05-05, there is a packaging issue on Debian:
// https://github.com/llvm/llvm-project/issues/62199
// A workaround is to fix this locally, using something like this:
//
// ln -sf ../../x86_64-linux-gnu/libclang-16.so.1 /usr/lib/llvm-16/lib/libclang.so
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-16/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@16/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@16/include
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
*/
import "C"
+5 -1
View File
@@ -77,6 +77,10 @@ CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
return clang_getEnumDeclIntegerType(c); return clang_getEnumDeclIntegerType(c);
} }
unsigned tinygo_clang_Cursor_isAnonymous(CXCursor c) {
return clang_Cursor_isAnonymous(c);
}
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) { unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
return clang_Cursor_isBitField(c); return clang_Cursor_isBitField(c);
} }
+2 -2
View File
@@ -51,10 +51,10 @@ type (
C.longlong int64 C.longlong int64
C.ulonglong uint64 C.ulonglong uint64
) )
type C._Ctype_struct___0 struct { type C.struct_point_t struct {
x C.int x C.int
y C.int y C.int
} }
type C.point_t = C._Ctype_struct___0 type C.point_t = C.struct_point_t
const C.SOME_CONST_3 = 1234 const C.SOME_CONST_3 = 1234
+34 -36
View File
@@ -38,11 +38,11 @@ type (
C.ulonglong uint64 C.ulonglong uint64
) )
type C.myint = C.int type C.myint = C.int
type C._Ctype_struct___0 struct { type C.struct_point2d_t struct {
x C.int x C.int
y C.int y C.int
} }
type C.point2d_t = C._Ctype_struct___0 type C.point2d_t = C.struct_point2d_t
type C.struct_point3d struct { type C.struct_point3d struct {
x C.int x C.int
y C.int y C.int
@@ -55,21 +55,19 @@ type C.struct_type1 struct {
___type C.int ___type C.int
} }
type C.struct_type2 struct{ _type C.int } type C.struct_type2 struct{ _type C.int }
type C._Ctype_union___1 struct{ i C.int } type C.union_union1_t struct{ i C.int }
type C.union1_t = C._Ctype_union___1 type C.union1_t = C.union_union1_t
type C._Ctype_union___2 struct{ $union uint64 } type C.union_union3_t struct{ $union uint64 }
func (union *C._Ctype_union___2) unionfield_i() *C.int { func (union *C.union_union3_t) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
return (*C.int)(unsafe.Pointer(&union.$union)) func (union *C.union_union3_t) unionfield_d() *float64 {
}
func (union *C._Ctype_union___2) unionfield_d() *float64 {
return (*float64)(unsafe.Pointer(&union.$union)) return (*float64)(unsafe.Pointer(&union.$union))
} }
func (union *C._Ctype_union___2) unionfield_s() *C.short { func (union *C.union_union3_t) unionfield_s() *C.short {
return (*C.short)(unsafe.Pointer(&union.$union)) return (*C.short)(unsafe.Pointer(&union.$union))
} }
type C.union3_t = C._Ctype_union___2 type C.union3_t = C.union_union3_t
type C.union_union2d struct{ $union [2]uint64 } type C.union_union2d struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) } func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
@@ -78,50 +76,50 @@ func (union *C.union_union2d) unionfield_d() *[2]float64 {
} }
type C.union2d_t = C.union_union2d type C.union2d_t = C.union_union2d
type C._Ctype_union___3 struct{ arr [10]C.uchar } type C.union_unionarray_t struct{ arr [10]C.uchar }
type C.unionarray_t = C._Ctype_union___3 type C.unionarray_t = C.union_unionarray_t
type C._Ctype_union___5 struct{ $union [3]uint32 } type C._Ctype_union___0 struct{ $union [3]uint32 }
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t { func (union *C._Ctype_union___0) unionfield_area() *C.point2d_t {
return (*C.point2d_t)(unsafe.Pointer(&union.$union)) return (*C.point2d_t)(unsafe.Pointer(&union.$union))
} }
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t { func (union *C._Ctype_union___0) unionfield_solid() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union)) return (*C.point3d_t)(unsafe.Pointer(&union.$union))
} }
type C._Ctype_struct___4 struct { type C.struct_struct_nested_t struct {
begin C.point2d_t begin C.point2d_t
end C.point2d_t end C.point2d_t
tag C.int tag C.int
coord C._Ctype_union___5 coord C._Ctype_union___0
} }
type C.struct_nested_t = C._Ctype_struct___4 type C.struct_nested_t = C.struct_struct_nested_t
type C._Ctype_union___6 struct{ $union [2]uint64 } type C.union_union_nested_t struct{ $union [2]uint64 }
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t { func (union *C.union_union_nested_t) unionfield_point() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union)) return (*C.point3d_t)(unsafe.Pointer(&union.$union))
} }
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t { func (union *C.union_union_nested_t) unionfield_array() *C.unionarray_t {
return (*C.unionarray_t)(unsafe.Pointer(&union.$union)) return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
} }
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t { func (union *C.union_union_nested_t) unionfield_thing() *C.union3_t {
return (*C.union3_t)(unsafe.Pointer(&union.$union)) return (*C.union3_t)(unsafe.Pointer(&union.$union))
} }
type C.union_nested_t = C._Ctype_union___6 type C.union_nested_t = C.union_union_nested_t
type C.enum_option = C.int type C.enum_option = C.int
type C.option_t = C.enum_option type C.option_t = C.enum_option
type C._Ctype_enum___7 = C.uint type C.enum_option2_t = C.uint
type C.option2_t = C._Ctype_enum___7 type C.option2_t = C.enum_option2_t
type C._Ctype_struct___8 struct { type C.struct_types_t struct {
f float32 f float32
d float64 d float64
ptr *C.int ptr *C.int
} }
type C.types_t = C._Ctype_struct___8 type C.types_t = C.struct_types_t
type C.myIntArray = [10]C.int type C.myIntArray = [10]C.int
type C._Ctype_struct___9 struct { type C.struct_bitfield_t struct {
start C.uchar start C.uchar
__bitfield_1 C.uchar __bitfield_1 C.uchar
@@ -129,21 +127,21 @@ type C._Ctype_struct___9 struct {
e C.uchar e C.uchar
} }
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f } func (s *C.struct_bitfield_t) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) { func (s *C.struct_bitfield_t) set_bitfield_a(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
} }
func (s *C._Ctype_struct___9) bitfield_b() C.uchar { func (s *C.struct_bitfield_t) bitfield_b() C.uchar {
return s.__bitfield_1 >> 5 & 0x1 return s.__bitfield_1 >> 5 & 0x1
} }
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) { func (s *C.struct_bitfield_t) set_bitfield_b(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
} }
func (s *C._Ctype_struct___9) bitfield_c() C.uchar { func (s *C.struct_bitfield_t) bitfield_c() C.uchar {
return s.__bitfield_1 >> 6 return s.__bitfield_1 >> 6
} }
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar, func (s *C.struct_bitfield_t) set_bitfield_c(value C.uchar,
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 } ) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
type C.bitfield_t = C._Ctype_struct___9 type C.bitfield_t = C.struct_bitfield_t
+6 -6
View File
@@ -145,18 +145,18 @@ func (c *Config) Serial() string {
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner // OptLevels returns the optimization level (0-2), size level (0-2), and inliner
// threshold as used in the LLVM optimization pipeline. // threshold as used in the LLVM optimization pipeline.
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) { func (c *Config) OptLevel() (level string, speedLevel, sizeLevel int) {
switch c.Options.Opt { switch c.Options.Opt {
case "none", "0": case "none", "0":
return 0, 0, 0 // -O0 return "O0", 0, 0
case "1": case "1":
return 1, 0, 0 // -O1 return "O1", 1, 0
case "2": case "2":
return 2, 0, 225 // -O2 return "O2", 2, 0
case "s": case "s":
return 2, 1, 225 // -Os return "Os", 2, 1
case "z": case "z":
return 2, 2, 5 // -Oz, default return "Oz", 2, 2 // default
default: default:
// This is not shown to the user: valid choices are already checked as // This is not shown to the user: valid choices are already checked as
// part of Options.Verify(). It is here as a sanity check. // part of Options.Verify(). It is here as a sanity check.
+7 -3
View File
@@ -281,10 +281,14 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
} }
case "arm64": case "arm64":
spec.CPU = "generic" spec.CPU = "generic"
spec.Features = "+neon" if goos == "darwin" {
spec.Features = "+neon"
} else { // windows, linux
spec.Features = "+neon,-fmv"
}
case "wasm": case "wasm":
spec.CPU = "generic" spec.CPU = "generic"
spec.Features = "+bulk-memory,+nontrapping-fptoint,+sign-ext" spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
spec.BuildTags = append(spec.BuildTags, "tinygo.wasm") spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
spec.CFlags = append(spec.CFlags, spec.CFlags = append(spec.CFlags,
"-mbulk-memory", "-mbulk-memory",
@@ -340,7 +344,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Linker = "wasm-ld" spec.Linker = "wasm-ld"
spec.RTLib = "compiler-rt" spec.RTLib = "compiler-rt"
spec.Libc = "wasi-libc" spec.Libc = "wasi-libc"
spec.DefaultStackSize = 1024 * 16 // 16kB spec.DefaultStackSize = 1024 * 32 // 32kB
spec.LDFlags = append(spec.LDFlags, spec.LDFlags = append(spec.LDFlags,
"--stack-first", "--stack-first",
"--no-demangle", "--no-demangle",
-28
View File
@@ -35,17 +35,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer": case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn)) val := b.getValue(b.fn.Params[1], getPos(b.fn))
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
if isPointer {
// atomicrmw only supports integers, so cast to an integer.
// TODO: this is fixed in LLVM 15.
val = b.CreatePtrToInt(val, b.uintptrType, "")
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
}
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true) oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
if isPointer {
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
}
return oldVal return oldVal
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer": case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
@@ -63,24 +53,6 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer": case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn)) val := b.getValue(b.fn.Params[1], getPos(b.fn))
if strings.HasPrefix(b.Triple, "avr") {
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
vType := val.Type()
isPointer := vType.TypeKind() == llvm.PointerTypeKind
if isPointer {
// libcalls only supports integers, so cast to an integer.
vType = b.uintptrType
val = b.CreatePtrToInt(val, vType, "")
ptr = b.CreateBitCast(ptr, llvm.PointerType(vType, 0), "")
}
name := fmt.Sprintf("__atomic_store_%d", vType.IntTypeWidth()/8)
fn := b.mod.NamedFunction(name)
if fn.IsNil() {
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
}
b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
return llvm.Value{}
}
store := b.CreateStore(val, ptr) store := b.CreateStore(val, ptr)
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent) store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
+1 -1
View File
@@ -45,7 +45,7 @@ func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name
if llvmFn.IsNil() { if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil)) panic("trying to call non-existent function: " + fn.RelString(nil))
} }
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter args = append(args, llvm.Undef(b.dataPtrType)) // unused context parameter
if isInvoke { if isInvoke {
return b.createInvoke(fnType, llvmFn, args, name) return b.createInvoke(fnType, llvmFn, args, name)
} }
+22 -26
View File
@@ -33,28 +33,27 @@ func (b *builder) createChanSend(instr *ssa.Send) {
// store value-to-send // store value-to-send
valueType := b.getLLVMType(instr.X.Type()) valueType := b.getLLVMType(instr.X.Type())
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0 isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value var valueAlloca, valueAllocaSize llvm.Value
if isZeroSize { if isZeroSize {
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0)) valueAlloca = llvm.ConstNull(b.dataPtrType)
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
} else { } else {
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value") valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
b.CreateStore(chanValue, valueAlloca) b.CreateStore(chanValue, valueAlloca)
} }
// Allocate blockedlist buffer. // Allocate blockedlist buffer.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList") channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the send. // Do the send.
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "") b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
// End the lifetime of the allocas. // End the lifetime of the allocas.
// This also works around a bug in CoroSplit, at least in LLVM 8: // This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742 // https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize) b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
if !isZeroSize { if !isZeroSize {
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
} }
} }
@@ -66,28 +65,27 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
// Allocate memory to receive into. // Allocate memory to receive into.
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0 isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value var valueAlloca, valueAllocaSize llvm.Value
if isZeroSize { if isZeroSize {
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0)) valueAlloca = llvm.ConstNull(b.dataPtrType)
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
} else { } else {
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value") valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
} }
// Allocate blockedlist buffer. // Allocate blockedlist buffer.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList") channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the receive. // Do the receive.
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "") commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
var received llvm.Value var received llvm.Value
if isZeroSize { if isZeroSize {
received = llvm.ConstNull(valueType) received = llvm.ConstNull(valueType)
} else { } else {
received = b.CreateLoad(valueType, valueAlloca, "chan.received") received = b.CreateLoad(valueType, valueAlloca, "chan.received")
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
} }
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize) b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
if unop.CommaOk { if unop.CommaOk {
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false)) tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
@@ -159,8 +157,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
sendValue := b.getValue(state.Send, state.Pos) sendValue := b.getValue(state.Send, state.Pos)
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value") alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
b.CreateStore(sendValue, alloca) b.CreateStore(sendValue, alloca)
ptr := b.CreateBitCast(alloca, b.i8ptrType, "") selectState = b.CreateInsertValue(selectState, alloca, 1, "")
selectState = b.CreateInsertValue(selectState, ptr, 1, "")
default: default:
panic("unreachable") panic("unreachable")
} }
@@ -168,10 +165,10 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
} }
// Create a receive buffer, where the received value will be stored. // Create a receive buffer, where the received value will be stored.
recvbuf := llvm.Undef(b.i8ptrType) recvbuf := llvm.Undef(b.dataPtrType)
if recvbufSize != 0 { if recvbufSize != 0 {
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize)) allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca") recvbufAlloca, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
recvbufAlloca.SetAlignment(recvbufAlign) recvbufAlloca.SetAlignment(recvbufAlign)
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{ recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
@@ -181,7 +178,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
// Create the states slice (allocated on the stack). // Create the states slice (allocated on the stack).
statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates)) statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates))
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca") statesAlloca, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
for i, state := range selectStates { for i, state := range selectStates {
// Set each slice element to the appropriate channel. // Set each slice element to the appropriate channel.
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{ gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
@@ -202,7 +199,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
// Stack-allocate operation structures. // Stack-allocate operation structures.
// If these were simply created as a slice, they would heap-allocate. // If these were simply created as a slice, they would heap-allocate.
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates)) chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca") chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false) chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{ chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
@@ -216,7 +213,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
}, "select.result") }, "select.result")
// Terminate the lifetime of the operation structures. // Terminate the lifetime of the operation structures.
b.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize) b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
} else { } else {
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{ results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
recvbuf, recvbuf,
@@ -225,7 +222,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
} }
// Terminate the lifetime of the states alloca. // Terminate the lifetime of the states alloca.
b.emitLifetimeEnd(statesI8, statesSize) b.emitLifetimeEnd(statesAlloca, statesSize)
// The result value does not include all the possible received values, // The result value does not include all the possible received values,
// because we can't load them in advance. Instead, the *ssa.Extract // because we can't load them in advance. Instead, the *ssa.Extract
@@ -265,7 +262,6 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
// it to the correct type, and dereference it. // it to the correct type, and dereference it.
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)] recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
typ := b.getLLVMType(expr.Type()) typ := b.getLLVMType(expr.Type())
ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "") return b.CreateLoad(typ, recvbuf, "")
return b.CreateLoad(typ, ptr, "")
} }
} }
+37 -57
View File
@@ -75,10 +75,9 @@ type compilerContext struct {
machine llvm.TargetMachine machine llvm.TargetMachine
targetData llvm.TargetData targetData llvm.TargetData
intType llvm.Type intType llvm.Type
i8ptrType llvm.Type // for convenience dataPtrType llvm.Type // pointer in address space 0
rawVoidFuncType llvm.Type // for convenience funcPtrType llvm.Type // pointer in function address space (1 for AVR, 0 elsewhere)
funcPtrAddrSpace int funcPtrAddrSpace int
hasTypedPointers bool // for LLVM 14 backwards compatibility
uintptrType llvm.Type uintptrType llvm.Type
program *ssa.Program program *ssa.Program
diagnostics []error diagnostics []error
@@ -123,13 +122,12 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
} else { } else {
panic("unknown pointer size") panic("unknown pointer size")
} }
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0) c.dataPtrType = llvm.PointerType(c.ctx.Int8Type(), 0)
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false) dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType) dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace() c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
c.hasTypedPointers = c.i8ptrType != llvm.PointerType(c.ctx.Int16Type(), 0) // with opaque pointers, all pointers are the same type (LLVM 15+) c.funcPtrType = dummyFunc.Type()
c.rawVoidFuncType = dummyFunc.Type()
dummyFunc.EraseFromParentAsFunction() dummyFunc.EraseFromParentAsFunction()
return c return c
@@ -417,16 +415,14 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
case types.Uintptr: case types.Uintptr:
return c.uintptrType return c.uintptrType
case types.UnsafePointer: case types.UnsafePointer:
return c.i8ptrType return c.dataPtrType
default: default:
panic("unknown basic type: " + typ.String()) panic("unknown basic type: " + typ.String())
} }
case *types.Chan: case *types.Chan, *types.Map, *types.Pointer:
return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0) return c.dataPtrType // all pointers are the same
case *types.Interface: case *types.Interface:
return c.getLLVMRuntimeType("_interface") return c.getLLVMRuntimeType("_interface")
case *types.Map:
return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0)
case *types.Named: case *types.Named:
if st, ok := typ.Underlying().(*types.Struct); ok { if st, ok := typ.Underlying().(*types.Struct); ok {
// Structs are a special case. While other named types are ignored // Structs are a special case. While other named types are ignored
@@ -441,21 +437,11 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
return llvmType return llvmType
} }
return c.getLLVMType(typ.Underlying()) return c.getLLVMType(typ.Underlying())
case *types.Pointer:
if c.hasTypedPointers {
ptrTo := c.getLLVMType(typ.Elem())
return llvm.PointerType(ptrTo, 0)
}
return c.i8ptrType // all pointers are the same
case *types.Signature: // function value case *types.Signature: // function value
return c.getFuncType(typ) return c.getFuncType(typ)
case *types.Slice: case *types.Slice:
ptrType := c.i8ptrType
if c.hasTypedPointers {
ptrType = llvm.PointerType(c.getLLVMType(typ.Elem()), 0)
}
members := []llvm.Type{ members := []llvm.Type{
ptrType, c.dataPtrType,
c.uintptrType, // len c.uintptrType, // len
c.uintptrType, // cap c.uintptrType, // cap
} }
@@ -545,8 +531,8 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
Elements: []llvm.Metadata{ Elements: []llvm.Metadata{
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{ c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "ptr", Name: "ptr",
SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8, SizeInBits: c.targetData.TypeAllocSize(c.dataPtrType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8, AlignInBits: uint32(c.targetData.ABITypeAlignment(c.dataPtrType)) * 8,
OffsetInBits: 0, OffsetInBits: 0,
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])), Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
}), }),
@@ -1548,21 +1534,18 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
src := argValues[0] src := argValues[0]
elems := argValues[1] elems := argValues[1]
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf") srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr")
srcLen := b.CreateExtractValue(src, 1, "append.srcLen") srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
srcCap := b.CreateExtractValue(src, 2, "append.srcCap") srcCap := b.CreateExtractValue(src, 2, "append.srcCap")
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf") elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr")
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen") elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new") result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
newPtr := b.CreateExtractValue(result, 0, "append.newPtr") newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
newBuf := b.CreateBitCast(newPtr, srcBuf.Type(), "append.newBuf")
newLen := b.CreateExtractValue(result, 1, "append.newLen") newLen := b.CreateExtractValue(result, 1, "append.newLen")
newCap := b.CreateExtractValue(result, 2, "append.newCap") newCap := b.CreateExtractValue(result, 2, "append.newCap")
newSlice := llvm.Undef(src.Type()) newSlice := llvm.Undef(src.Type())
newSlice = b.CreateInsertValue(newSlice, newBuf, 0, "") newSlice = b.CreateInsertValue(newSlice, newPtr, 0, "")
newSlice = b.CreateInsertValue(newSlice, newLen, 1, "") newSlice = b.CreateInsertValue(newSlice, newLen, 1, "")
newSlice = b.CreateInsertValue(newSlice, newCap, 2, "") newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
return newSlice, nil return newSlice, nil
@@ -1610,9 +1593,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
// The pointer to the data to be cleared. // The pointer to the data to be cleared.
llvmBuf := b.CreateExtractValue(value, 0, "buf") llvmBuf := b.CreateExtractValue(value, 0, "buf")
if llvmBuf.Type() != b.i8ptrType { // compatibility with LLVM 14
llvmBuf = b.CreateBitCast(llvmBuf, b.i8ptrType, "")
}
// The length (in bytes) to be cleared. // The length (in bytes) to be cleared.
llvmLen := b.CreateExtractValue(value, 1, "len") llvmLen := b.CreateExtractValue(value, 1, "len")
@@ -1647,8 +1627,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray") dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray") srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr")
srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr")
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
case "delete": case "delete":
@@ -1888,14 +1866,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
switch value := instr.Value.(type) { switch value := instr.Value.(type) {
case *ssa.Function: case *ssa.Function:
// Regular function call. No context is necessary. // Regular function call. No context is necessary.
context = llvm.Undef(b.i8ptrType) context = llvm.Undef(b.dataPtrType)
if info.variadic && len(fn.Params) == 0 { if info.variadic && len(fn.Params) == 0 {
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq // This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
// Eventually we might be able to eliminate this special case // Eventually we might be able to eliminate this special case
// entirely. For details, see: // entirely. For details, see:
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521 // https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false) calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(calleeType, b.funcPtrAddrSpace))
} }
case *ssa.MakeClosure: case *ssa.MakeClosure:
// A call on a func value, but the callee is trivial to find. For // A call on a func value, but the callee is trivial to find. For
@@ -1923,13 +1900,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
params = append(params, typecode) params = append(params, typecode)
callee = b.getInvokeFunction(instr) callee = b.getInvokeFunction(instr)
calleeType = callee.GlobalValueType() calleeType = callee.GlobalValueType()
context = llvm.Undef(b.i8ptrType) context = llvm.Undef(b.dataPtrType)
} else { } else {
// Function pointer. // Function pointer.
value := b.getValue(instr.Value, getPos(instr)) value := b.getValue(instr.Value, getPos(instr))
// This is a func value, which cannot be called directly. We have to // This is a func value, which cannot be called directly. We have to
// extract the function pointer and context first from the func value. // extract the function pointer and context first from the func value.
calleeType, callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) callee, context = b.decodeFuncValue(value)
calleeType = b.getLLVMFunctionType(instr.Value.Type().Underlying().(*types.Signature))
b.createNilCheck(instr.Value, callee, "fpcall") b.createNilCheck(instr.Value, callee, "fpcall")
} }
@@ -1962,7 +1940,7 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
return llvm.Undef(b.getLLVMType(expr.Type())) return llvm.Undef(b.getLLVMType(expr.Type()))
} }
_, fn := b.getFunction(expr) _, fn := b.getFunction(expr)
return b.createFuncValue(fn, llvm.Undef(b.i8ptrType), expr.Signature) return b.createFuncValue(fn, llvm.Undef(b.dataPtrType), expr.Signature)
case *ssa.Global: case *ssa.Global:
value := b.getGlobal(expr) value := b.getGlobal(expr)
if value.IsNil() { if value.IsNil() {
@@ -2030,7 +2008,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sizeValue := llvm.ConstInt(b.uintptrType, size, false) sizeValue := llvm.ConstInt(b.uintptrType, size, false)
layoutValue := b.createObjectLayout(typ, expr.Pos()) layoutValue := b.createObjectLayout(typ, expr.Pos())
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment) buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
return buf, nil return buf, nil
} else { } else {
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment) buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
@@ -2076,10 +2053,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
value = b.CreateInsertValue(value, field, i, "changetype.struct") value = b.CreateInsertValue(value, field, i, "changetype.struct")
} }
return value, nil return value, nil
case llvm.PointerTypeKind:
// This can happen with pointers to structs. This case is easy:
// simply bitcast the pointer to the destination type.
return b.CreateBitCast(x, llvmType, "changetype.pointer"), nil
default: default:
return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String()) return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String())
} }
@@ -2156,12 +2129,12 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
// Can't load directly from array (as index is non-constant), so // Can't load directly from array (as index is non-constant), so
// have to do it using an alloca+gep+load. // have to do it using an alloca+gep+load.
arrayType := collection.Type() arrayType := collection.Type()
alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca") alloca, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
b.CreateStore(collection, alloca) b.CreateStore(collection, alloca)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep") ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load") result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
b.emitLifetimeEnd(allocaPtr, allocaSize) b.emitLifetimeEnd(alloca, allocaSize)
return result, nil return result, nil
default: default:
panic("unknown *ssa.Index type") panic("unknown *ssa.Index type")
@@ -2264,7 +2237,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap") sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos()) layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf") slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array")
// Extend or truncate if necessary. This is safe as we've already done // Extend or truncate if necessary. This is safe as we've already done
// the bounds check. // the bounds check.
@@ -2310,7 +2282,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
default: default:
panic("unknown type in range: " + typ.String()) panic("unknown type in range: " + typ.String())
} }
it, _, _ := b.createTemporaryAlloca(iteratorType, "range.it") it, _ := b.createTemporaryAlloca(iteratorType, "range.it")
b.CreateStore(llvm.ConstNull(iteratorType), it) b.CreateStore(llvm.ConstNull(iteratorType), it)
return it, nil return it, nil
case *ssa.Select: case *ssa.Select:
@@ -2478,7 +2450,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len() arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
b.createSliceToArrayPointerCheck(sliceLen, arrayLen) b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
ptr := b.CreateExtractValue(slice, 0, "") ptr := b.CreateExtractValue(slice, 0, "")
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
return ptr, nil return ptr, nil
case *ssa.TypeAssert: case *ssa.TypeAssert:
return b.createTypeAssert(expr), nil return b.createTypeAssert(expr), nil
@@ -2944,16 +2915,16 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero}) strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
} else { } else {
strPtr = llvm.ConstNull(c.i8ptrType) strPtr = llvm.ConstNull(c.dataPtrType)
} }
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
return strObj return strObj
} else if typ.Kind() == types.UnsafePointer { } else if typ.Kind() == types.UnsafePointer {
if !expr.IsNil() { if !expr.IsNil() {
value, _ := constant.Uint64Val(constant.ToInt(expr.Value)) value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType) return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.dataPtrType)
} }
return llvm.ConstNull(c.i8ptrType) return llvm.ConstNull(c.dataPtrType)
} else if typ.Info()&types.IsUnsigned != 0 { } else if typ.Info()&types.IsUnsigned != 0 {
n, _ := constant.Uint64Val(constant.ToInt(expr.Value)) n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstInt(llvmType, n, false) return llvm.ConstInt(llvmType, n, false)
@@ -2997,7 +2968,7 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
// Create a generic nil interface with no dynamic type (typecode=0). // Create a generic nil interface with no dynamic type (typecode=0).
fields := []llvm.Value{ fields := []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstPointerNull(c.i8ptrType), llvm.ConstPointerNull(c.dataPtrType),
} }
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields) return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
case *types.Pointer: case *types.Pointer:
@@ -3005,12 +2976,16 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
panic("expected nil pointer constant") panic("expected nil pointer constant")
} }
return llvm.ConstPointerNull(c.getLLVMType(typ)) return llvm.ConstPointerNull(c.getLLVMType(typ))
case *types.Array:
if expr.Value != nil {
panic("expected nil array constant")
}
return llvm.ConstNull(c.getLLVMType(expr.Type()))
case *types.Slice: case *types.Slice:
if expr.Value != nil { if expr.Value != nil {
panic("expected nil slice constant") panic("expected nil slice constant")
} }
elemType := c.getLLVMType(typ.Elem()) llvmPtr := llvm.ConstPointerNull(c.dataPtrType)
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
llvmLen := llvm.ConstInt(c.uintptrType, 0, false) llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
slice := c.ctx.ConstStruct([]llvm.Value{ slice := c.ctx.ConstStruct([]llvm.Value{
llvmPtr, // backing array llvmPtr, // backing array
@@ -3018,6 +2993,11 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
llvmLen, // cap llvmLen, // cap
}, false) }, false)
return slice return slice
case *types.Struct:
if expr.Value != nil {
panic("expected nil struct constant")
}
return llvm.ConstNull(c.getLLVMType(expr.Type()))
case *types.Map: case *types.Map:
if !expr.IsNil() { if !expr.IsNil() {
// I believe this is not allowed by the Go spec. // I believe this is not allowed by the Go spec.
@@ -3046,7 +3026,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p
// Conversion between pointers and unsafe.Pointer. // Conversion between pointers and unsafe.Pointer.
if isPtrFrom && isPtrTo { if isPtrFrom && isPtrTo {
return b.CreateBitCast(value, llvmTypeTo, ""), nil return value, nil
} }
switch typeTo := typeTo.Underlying().(type) { switch typeTo := typeTo.Underlying().(type) {
@@ -3285,7 +3265,7 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
if fn.IsNil() { if fn.IsNil() {
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name) return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
} }
return b.CreateBitCast(fn, b.i8ptrType, ""), nil return fn, nil
} else { } else {
b.createNilCheck(unop.X, x, "deref") b.createNilCheck(unop.X, x, "deref")
load := b.CreateLoad(valueType, x, "") load := b.CreateLoad(valueType, x, "")
+7 -9
View File
@@ -91,14 +91,12 @@ func TestCompiler(t *testing.T) {
} }
// Optimize IR a little. // Optimize IR a little.
funcPasses := llvm.NewFunctionPassManagerForModule(mod) passOptions := llvm.NewPassBuilderOptions()
defer funcPasses.Dispose() defer passOptions.Dispose()
funcPasses.AddInstructionCombiningPass() err = mod.RunPasses("instcombine", llvm.TargetMachine{}, passOptions)
funcPasses.InitializeFunc() if err != nil {
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { t.Error(err)
funcPasses.RunFunc(fn)
} }
funcPasses.FinalizeFunc()
outFilePrefix := tc.file[:len(tc.file)-3] outFilePrefix := tc.file[:len(tc.file)-3]
if tc.target != "" { if tc.target != "" {
@@ -169,9 +167,9 @@ func filterIrrelevantIRLines(lines []string) []string {
if strings.HasPrefix(line, "source_filename = ") { if strings.HasPrefix(line, "source_filename = ") {
continue continue
} }
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") { if llvmVersion < 15 && strings.HasPrefix(line, "target datalayout = ") {
// The datalayout string may vary betewen LLVM versions. // The datalayout string may vary betewen LLVM versions.
// Right now test outputs are for LLVM 14 and higher. // Right now test outputs are for LLVM 15 and higher.
continue continue
} }
out = append(out, line) out = append(out, line)
+24 -33
View File
@@ -60,9 +60,8 @@ func (b *builder) deferInitFunc() {
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin) b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
// Create defer list pointer. // Create defer list pointer.
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
b.deferPtr = b.CreateAlloca(deferType, "deferPtr") b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
if b.hasDeferFrame() { if b.hasDeferFrame() {
// Set up the defer frame with the current stack pointer. // Set up the defer frame with the current stack pointer.
@@ -249,8 +248,7 @@ func isInLoop(start *ssa.BasicBlock) bool {
func (b *builder) createDefer(instr *ssa.Defer) { func (b *builder) createDefer(instr *ssa.Defer) {
// The pointer to the previous defer struct, which we will replace to // The pointer to the previous defer struct, which we will replace to
// make a linked list. // make a linked list.
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
next := b.CreateLoad(deferType, b.deferPtr, "defer.next")
var values []llvm.Value var values []llvm.Value
valueTypes := []llvm.Type{b.uintptrType, next.Type()} valueTypes := []llvm.Type{b.uintptrType, next.Type()}
@@ -271,7 +269,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode") typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
values = []llvm.Value{callback, next, typecode, receiverValue} values = []llvm.Value{callback, next, typecode, receiverValue}
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType) valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
for _, arg := range instr.Call.Args { for _, arg := range instr.Call.Args {
val := b.getValue(arg, getPos(instr)) val := b.getValue(arg, getPos(instr))
values = append(values, val) values = append(values, val)
@@ -391,9 +389,8 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// This may be hit a variable number of times, so use a heap allocation. // This may be hit a variable number of times, so use a heap allocation.
size := b.targetData.TypeAllocSize(deferredCallType) size := b.targetData.TypeAllocSize(deferredCallType)
sizeValue := llvm.ConstInt(b.uintptrType, size, false) sizeValue := llvm.ConstInt(b.uintptrType, size, false)
nilPtr := llvm.ConstNull(b.i8ptrType) nilPtr := llvm.ConstNull(b.dataPtrType)
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call") alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferredCallType, 0), "defer.alloc")
} }
if b.NeedsStackObjects { if b.NeedsStackObjects {
b.trackPointer(alloca) b.trackPointer(alloca)
@@ -401,14 +398,12 @@ func (b *builder) createDefer(instr *ssa.Defer) {
b.CreateStore(deferredCall, alloca) b.CreateStore(deferredCall, alloca)
// Push it on top of the linked list by replacing deferPtr. // Push it on top of the linked list by replacing deferPtr.
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast") b.CreateStore(alloca, b.deferPtr)
b.CreateStore(allocaCast, b.deferPtr)
} }
// createRunDefers emits code to run all deferred functions. // createRunDefers emits code to run all deferred functions.
func (b *builder) createRunDefers() { func (b *builder) createRunDefers() {
deferType := b.getLLVMRuntimeType("_defer") deferType := b.getLLVMRuntimeType("_defer")
deferPtrType := llvm.PointerType(deferType, 0)
// Add a loop like the following: // Add a loop like the following:
// for stack != nil { // for stack != nil {
@@ -435,7 +430,7 @@ func (b *builder) createRunDefers() {
// Create loop head: // Create loop head:
// for stack != nil { // for stack != nil {
b.SetInsertPointAtEnd(loophead) b.SetInsertPointAtEnd(loophead)
deferData := b.CreateLoad(deferPtrType, b.deferPtr, "") deferData := b.CreateLoad(b.dataPtrType, b.deferPtr, "")
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil") stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
b.CreateCondBr(stackIsNil, end, loop) b.CreateCondBr(stackIsNil, end, loop)
@@ -448,7 +443,7 @@ func (b *builder) createRunDefers() {
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
}, "stack.next.gep") }, "stack.next.gep")
nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next") nextStack := b.CreateLoad(b.dataPtrType, nextStackGEP, "stack.next")
b.CreateStore(nextStack, b.deferPtr) b.CreateStore(nextStack, b.deferPtr)
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{ gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
@@ -469,28 +464,26 @@ func (b *builder) createRunDefers() {
// Call on an value or interface value. // Call on an value or interface value.
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
if !callback.IsInvoke() { if !callback.IsInvoke() {
//Expect funcValue to be passed through the deferred call. //Expect funcValue to be passed through the deferred call.
valueTypes = append(valueTypes, b.getFuncType(callback.Signature())) valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
} else { } else {
//Expect typecode //Expect typecode
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType) valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
} }
for _, arg := range callback.Args { for _, arg := range callback.Args {
valueTypes = append(valueTypes, b.getLLVMType(arg.Type())) valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
} }
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct (including receiver). // Extract the params from the struct (including receiver).
forwardParams := []llvm.Value{} forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
deferredCallType := b.ctx.StructType(valueTypes, false)
for i := 2; i < len(valueTypes); i++ { for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep") gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i], gep, "param") forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
forwardParams = append(forwardParams, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
@@ -505,7 +498,8 @@ func (b *builder) createRunDefers() {
//Get function pointer and context //Get function pointer and context
var context llvm.Value var context llvm.Value
fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature()) fnPtr, context = b.decodeFuncValue(funcValue)
fnType = b.getLLVMFunctionType(callback.Signature())
//Pass context //Pass context
forwardParams = append(forwardParams, context) forwardParams = append(forwardParams, context)
@@ -519,7 +513,7 @@ func (b *builder) createRunDefers() {
// Add the context parameter. An interface call cannot also be a // Add the context parameter. An interface call cannot also be a
// closure but we have to supply the parameter anyway for platforms // closure but we have to supply the parameter anyway for platforms
// with a strict calling convention. // with a strict calling convention.
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
} }
b.createCall(fnType, fnPtr, forwardParams, "") b.createCall(fnType, fnPtr, forwardParams, "")
@@ -528,18 +522,17 @@ func (b *builder) createRunDefers() {
// Direct call. // Direct call.
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
for _, param := range getParams(callback.Signature) { for _, param := range getParams(callback.Signature) {
valueTypes = append(valueTypes, b.getLLVMType(param.Type())) valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
} }
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct. // Extract the params from the struct.
forwardParams := []llvm.Value{} forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := range getParams(callback.Signature) { for i := range getParams(callback.Signature) {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param") forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
forwardParams = append(forwardParams, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
@@ -549,7 +542,7 @@ func (b *builder) createRunDefers() {
if !b.getFunctionInfo(callback).exported { if !b.getFunctionInfo(callback).exported {
// Add the context parameter. We know it is ignored by the receiving // Add the context parameter. We know it is ignored by the receiving
// function, but we have to pass one anyway. // function, but we have to pass one anyway.
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
} }
// Call real function. // Call real function.
@@ -559,20 +552,19 @@ func (b *builder) createRunDefers() {
case *ssa.MakeClosure: case *ssa.MakeClosure:
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
fn := callback.Fn.(*ssa.Function) fn := callback.Fn.(*ssa.Function)
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
params := fn.Signature.Params() params := fn.Signature.Params()
for i := 0; i < params.Len(); i++ { for i := 0; i < params.Len(); i++ {
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
} }
valueTypes = append(valueTypes, b.i8ptrType) // closure valueTypes = append(valueTypes, b.dataPtrType) // closure
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct. // Extract the params from the struct.
forwardParams := []llvm.Value{} forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 2; i < len(valueTypes); i++ { for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
forwardParam := b.CreateLoad(valueTypes[i], gep, "param") forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
forwardParams = append(forwardParams, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
@@ -584,7 +576,7 @@ func (b *builder) createRunDefers() {
db := b.deferBuiltinFuncs[callback] db := b.deferBuiltinFuncs[callback]
//Get parameter types //Get parameter types
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
//Get signature from call results //Get signature from call results
params := callback.Type().Underlying().(*types.Signature).Params() params := callback.Type().Underlying().(*types.Signature).Params()
@@ -593,13 +585,12 @@ func (b *builder) createRunDefers() {
} }
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct. // Extract the params from the struct.
var argValues []llvm.Value var argValues []llvm.Value
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < params.Len(); i++ { for i := 0; i < params.Len(); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param") forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
argValues = append(argValues, forwardParam) argValues = append(argValues, forwardParam)
} }
+10 -39
View File
@@ -13,35 +13,14 @@ import (
// createFuncValue creates a function value from a raw function pointer with no // createFuncValue creates a function value from a raw function pointer with no
// context. // context.
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
return b.compilerContext.createFuncValue(b.Builder, funcPtr, context, sig)
}
// createFuncValue creates a function value from a raw function pointer with no
// context.
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
// Closure is: {context, function pointer} // Closure is: {context, function pointer}
funcValueScalar := llvm.ConstBitCast(funcPtr, c.rawVoidFuncType) funcValueType := b.getFuncType(sig)
funcValueType := c.getFuncType(sig)
funcValue := llvm.Undef(funcValueType) funcValue := llvm.Undef(funcValueType)
funcValue = builder.CreateInsertValue(funcValue, context, 0, "") funcValue = b.CreateInsertValue(funcValue, context, 0, "")
funcValue = builder.CreateInsertValue(funcValue, funcValueScalar, 1, "") funcValue = b.CreateInsertValue(funcValue, funcPtr, 1, "")
return funcValue return funcValue
} }
// getFuncSignatureID returns a new external global for a given signature. This
// global reference is not real, it is only used during func lowering to assign
// signature types to functions and will then be removed.
func (c *compilerContext) getFuncSignatureID(sig *types.Signature) llvm.Value {
s, _ := getTypeCodeName(sig)
sigGlobalName := "reflect/types.funcid:" + s
sigGlobal := c.mod.NamedGlobal(sigGlobalName)
if sigGlobal.IsNil() {
sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName)
sigGlobal.SetGlobalConstant(true)
}
return sigGlobal
}
// extractFuncScalar returns some scalar that can be used in comparisons. It is // extractFuncScalar returns some scalar that can be used in comparisons. It is
// a cheap operation. // a cheap operation.
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value { func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
@@ -55,28 +34,20 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
} }
// decodeFuncValue extracts the context and the function pointer from this func // decodeFuncValue extracts the context and the function pointer from this func
// value. This may be an expensive operation. // value.
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcType llvm.Type, funcPtr, context llvm.Value) { func (b *builder) decodeFuncValue(funcValue llvm.Value) (funcPtr, context llvm.Value) {
context = b.CreateExtractValue(funcValue, 0, "") context = b.CreateExtractValue(funcValue, 0, "")
funcPtr = b.CreateExtractValue(funcValue, 1, "") funcPtr = b.CreateExtractValue(funcValue, 1, "")
if !funcPtr.IsAConstantExpr().IsNil() && funcPtr.Opcode() == llvm.BitCast {
funcPtr = funcPtr.Operand(0) // needed for LLVM 14 (no opaque pointers)
}
if sig != nil {
funcType = b.getRawFuncType(sig)
llvmSig := llvm.PointerType(funcType, b.funcPtrAddrSpace)
funcPtr = b.CreateBitCast(funcPtr, llvmSig, "")
}
return return
} }
// getFuncType returns the type of a func value given a signature. // getFuncType returns the type of a func value given a signature.
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type { func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false) return c.ctx.StructType([]llvm.Type{c.dataPtrType, c.funcPtrType}, false)
} }
// getRawFuncType returns a LLVM function type for a given signature. // getLLVMFunctionType returns a LLVM function type for a given signature.
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type { func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
// Get the return type. // Get the return type.
var returnType llvm.Type var returnType llvm.Type
switch typ.Results().Len() { switch typ.Results().Len() {
@@ -104,7 +75,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
if recv.StructName() == "runtime._interface" { if recv.StructName() == "runtime._interface" {
// This is a call on an interface, not a concrete type. // This is a call on an interface, not a concrete type.
// The receiver is not an interface, but a i8* type. // The receiver is not an interface, but a i8* type.
recv = c.i8ptrType recv = c.dataPtrType
} }
for _, info := range c.expandFormalParamType(recv, "", nil) { for _, info := range c.expandFormalParamType(recv, "", nil) {
paramTypes = append(paramTypes, info.llvmType) paramTypes = append(paramTypes, info.llvmType)
@@ -117,7 +88,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
} }
} }
// All functions take these parameters at the end. // All functions take these parameters at the end.
paramTypes = append(paramTypes, c.i8ptrType) // context paramTypes = append(paramTypes, c.dataPtrType) // context
// Make a func type out of the signature. // Make a func type out of the signature.
return llvm.FunctionType(returnType, paramTypes, false) return llvm.FunctionType(returnType, paramTypes, false)
-3
View File
@@ -81,9 +81,6 @@ func (b *builder) trackValue(value llvm.Value) {
// trackPointer creates a call to runtime.trackPointer, bitcasting the poitner // trackPointer creates a call to runtime.trackPointer, bitcasting the poitner
// first if needed. The input value must be of LLVM pointer type. // first if needed. The input value must be of LLVM pointer type.
func (b *builder) trackPointer(value llvm.Value) { func (b *builder) trackPointer(value llvm.Value) {
if value.Type() != b.i8ptrType {
value = b.CreateBitCast(value, b.i8ptrType, "")
}
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "") b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
} }
+13 -12
View File
@@ -21,7 +21,7 @@ func (b *builder) createGo(instr *ssa.Go) {
var prefix string var prefix string
var funcPtr llvm.Value var funcPtr llvm.Value
var funcPtrType llvm.Type var funcType llvm.Type
hasContext := false hasContext := false
if callee := instr.Call.StaticCallee(); callee != nil { if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new // Static callee is known. This makes it easier to start a new
@@ -42,7 +42,7 @@ func (b *builder) createGo(instr *ssa.Go) {
params = append(params, context) // context parameter params = append(params, context) // context parameter
hasContext = true hasContext = true
} }
funcPtrType, funcPtr = b.getFunction(callee) funcType, funcPtr = b.getFunction(callee)
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
// We cheat. None of the builtins do any long or blocking operation, so // We cheat. None of the builtins do any long or blocking operation, so
// we might as well run these builtins right away without the program // we might as well run these builtins right away without the program
@@ -80,7 +80,7 @@ func (b *builder) createGo(instr *ssa.Go) {
itfTypeCode := b.CreateExtractValue(itf, 0, "") itfTypeCode := b.CreateExtractValue(itf, 0, "")
itfValue := b.CreateExtractValue(itf, 1, "") itfValue := b.CreateExtractValue(itf, 1, "")
funcPtr = b.getInvokeFunction(&instr.Call) funcPtr = b.getInvokeFunction(&instr.Call)
funcPtrType = funcPtr.GlobalValueType() funcType = funcPtr.GlobalValueType()
params = append([]llvm.Value{itfValue}, params...) // start with receiver params = append([]llvm.Value{itfValue}, params...) // start with receiver
params = append(params, itfTypeCode) // end with typecode params = append(params, itfTypeCode) // end with typecode
} else { } else {
@@ -90,7 +90,8 @@ func (b *builder) createGo(instr *ssa.Go) {
// * The function context, for closures. // * The function context, for closures.
// * The function pointer (for tasks). // * The function pointer (for tasks).
var context llvm.Value var context llvm.Value
funcPtrType, funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)), instr.Call.Value.Type().Underlying().(*types.Signature)) 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) params = append(params, context, funcPtr)
hasContext = true hasContext = true
prefix = b.fn.RelString(nil) prefix = b.fn.RelString(nil)
@@ -98,14 +99,14 @@ func (b *builder) createGo(instr *ssa.Go) {
paramBundle := b.emitPointerPack(params) paramBundle := b.emitPointerPack(params)
var stackSize llvm.Value var stackSize llvm.Value
callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos()) callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, instr.Pos())
if b.AutomaticStackSize { if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy // The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF // function that will be replaced with a load from a special ELF
// section that contains the stack size (and is modified after // section that contains the stack size (and is modified after
// linking). // linking).
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function)) stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize") stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.dataPtrType)}, "stacksize")
} else { } else {
// The stack size is fixed at compile time. By emitting it here as a // The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized. // constant, it can be optimized.
@@ -115,7 +116,7 @@ func (b *builder) createGo(instr *ssa.Go) {
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false) stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
} }
fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function)) fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "") b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
} }
// createGoroutineStartWrapper creates a wrapper for the task-based // createGoroutineStartWrapper creates a wrapper for the task-based
@@ -165,7 +166,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
} }
// Create the wrapper. // Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType) wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
c.addStandardAttributes(wrapper) c.addStandardAttributes(wrapper)
wrapper.SetLinkage(llvm.LinkOnceODRLinkage) wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
@@ -203,7 +204,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
} }
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes) params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
if !hasContext { if !hasContext {
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
} }
// Create the call. // Create the call.
@@ -211,7 +212,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if c.Scheduler == "asyncify" { if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{ b.CreateCall(deadlockType, deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType), llvm.Undef(c.dataPtrType),
}, "") }, "")
} }
@@ -234,7 +235,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
// merged into one. // merged into one.
// Create the wrapper. // Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType) wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
c.addStandardAttributes(wrapper) c.addStandardAttributes(wrapper)
wrapper.SetLinkage(llvm.LinkOnceODRLinkage) wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
@@ -279,7 +280,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if c.Scheduler == "asyncify" { if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{ b.CreateCall(deadlockType, deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType), llvm.Undef(c.dataPtrType),
}, "") }, "")
} }
} }
+2 -2
View File
@@ -99,8 +99,8 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
case llvm.IntegerTypeKind: case llvm.IntegerTypeKind:
constraints = append(constraints, "r") constraints = append(constraints, "r")
case llvm.PointerTypeKind: case llvm.PointerTypeKind:
// Memory references require a type in LLVM 14, probably as a // Memory references require a type starting with LLVM 14,
// preparation for opaque pointers. // probably as a preparation for opaque pointers.
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23") err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
return s return s
default: default:
+11 -9
View File
@@ -147,7 +147,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.addError(token.NoPos, fmt.Sprintf("too many levels of pointers for typecode: %s", typstr)) c.addError(token.NoPos, fmt.Sprintf("too many levels of pointers for typecode: %s", typstr))
} }
return llvm.ConstGEP(c.ctx.Int8Type(), ptr, []llvm.Value{ return llvm.ConstGEP(c.ctx.Int8Type(), ptr, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 1, false), llvm.ConstInt(c.ctx.Int32Type(), 1, false),
}) })
} }
} }
@@ -218,6 +218,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]), types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
types.NewVar(token.NoPos, nil, "sliceOf", types.Typ[types.UnsafePointer]),
) )
case *types.Map: case *types.Map:
typeFieldTypes = append(typeFieldTypes, typeFieldTypes = append(typeFieldTypes,
@@ -326,6 +327,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.getTypeCode(types.NewPointer(typ)), // ptrTo c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elementType c.getTypeCode(typ.Elem()), // elementType
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
c.getTypeCode(types.NewSlice(typ.Elem())), // slicePtr
} }
case *types.Map: case *types.Map:
typeFields = []llvm.Value{ typeFields = []llvm.Value{
@@ -412,10 +414,10 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
}, typeFields...) }, typeFields...)
if hasMethodSet { if hasMethodSet {
typeFields = append([]llvm.Value{ typeFields = append([]llvm.Value{
llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType), c.getTypeMethodSet(typ),
}, typeFields...) }, typeFields...)
} }
alignment := c.targetData.TypeAllocSize(c.i8ptrType) alignment := c.targetData.TypeAllocSize(c.dataPtrType)
if alignment < 4 { if alignment < 4 {
alignment = 4 alignment = 4
} }
@@ -450,8 +452,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
offset = 1 offset = 1
} }
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{ return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(llvm.Int32Type(), offset, false), llvm.ConstInt(c.ctx.Int32Type(), offset, false),
}) })
} }
@@ -626,7 +628,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
// Construct global value. // Construct global value.
globalValue := c.ctx.ConstStruct([]llvm.Value{ globalValue := c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false), llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
llvm.ConstArray(c.i8ptrType, signatures), llvm.ConstArray(c.dataPtrType, signatures),
c.ctx.ConstStruct(wrappers, false), c.ctx.ConstStruct(wrappers, false),
}, false) }, false)
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName) global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
@@ -777,7 +779,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll
fnName := s + ".$typeassert" fnName := s + ".$typeassert"
llvmFn := c.mod.NamedFunction(fnName) llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false) llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn) c.addStandardDeclaredAttributes(llvmFn)
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface)) methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
@@ -800,7 +802,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
paramTuple = append(paramTuple, sig.Params().At(i)) paramTuple = append(paramTuple, sig.Params().At(i))
} }
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer])) paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)) llvmFnType := c.getLLVMFunctionType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn) c.addStandardDeclaredAttributes(llvmFn)
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method))) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
@@ -840,7 +842,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
} }
// create wrapper function // create wrapper function
paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...) paramTypes := append([]llvm.Type{c.dataPtrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false) wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType) wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
c.addStandardAttributes(wrapper) c.addStandardAttributes(wrapper)
+1 -1
View File
@@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Fall back to a generic error. // Fall back to a generic error.
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
} }
_, funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil) funcRawPtr, funcContext := b.decodeFuncValue(funcValue)
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType) funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
// Create a new global of type runtime/interrupt.handle. Globals of this // Create a new global of type runtime/interrupt.handle. Globals of this
+3 -10
View File
@@ -7,7 +7,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -48,12 +47,9 @@ func (b *builder) defineIntrinsicFunction() {
func (b *builder) createMemoryCopyImpl() { func (b *builder) createMemoryCopyImpl() {
b.createFunctionStart(true) b.createFunctionStart(true)
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
}
llvmFn := b.mod.NamedFunction(fnName) llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType) llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
} }
var params []llvm.Value var params []llvm.Value
@@ -84,12 +80,9 @@ func (b *builder) createMemoryZeroImpl() {
// Return the llvm.memset.p0.i8 function declaration. // Return the llvm.memset.p0.i8 function declaration.
func (c *compilerContext) getMemsetFunc() llvm.Value { func (c *compilerContext) getMemsetFunc() llvm.Value {
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth()) fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
}
llvmFn := c.mod.NamedFunction(fnName) llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false) fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, fnType) llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
} }
return llvmFn return llvmFn
@@ -111,7 +104,7 @@ func (b *builder) createKeepAliveImpl() {
// //
// It should be portable to basically everything as the "r" register type // It should be portable to basically everything as the "r" register type
// exists basically everywhere. // exists basically everywhere.
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false) asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false) asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "") b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
+39 -44
View File
@@ -20,7 +20,7 @@ import (
// //
// This is useful for creating temporary allocas for intrinsics. Don't forget to // This is useful for creating temporary allocas for intrinsics. Don't forget to
// end the lifetime using emitLifetimeEnd after you're done with it. // end the lifetime using emitLifetimeEnd after you're done with it.
func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, size llvm.Value) {
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name) return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
} }
@@ -63,47 +63,45 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
// Allocate memory for the packed data. // Allocate memory for the packed data.
size := b.targetData.TypeAllocSize(packedType) size := b.targetData.TypeAllocSize(packedType)
if size == 0 { if size == 0 {
return llvm.ConstPointerNull(b.i8ptrType) return llvm.ConstPointerNull(b.dataPtrType)
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind { } else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
return b.CreateBitCast(values[0], b.i8ptrType, "pack.ptr") return values[0]
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) { } else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
// Packed data fits in a pointer, so store it directly inside the // Packed data fits in a pointer, so store it directly inside the
// pointer. // pointer.
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind { if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
// Try to keep this cast in SSA form. // Try to keep this cast in SSA form.
return b.CreateIntToPtr(values[0], b.i8ptrType, "pack.int") return b.CreateIntToPtr(values[0], b.dataPtrType, "pack.int")
} }
// Because packedType is a struct and we have to cast it to a *i8, store // Because packedType is a struct and we have to cast it to a *i8, store
// it in a *i8 alloca first and load the *i8 value from there. This is // it in a *i8 alloca first and load the *i8 value from there. This is
// effectively a bitcast. // effectively a bitcast.
packedAlloc, _, _ := b.createTemporaryAlloca(b.i8ptrType, "") packedAlloc, _ := b.createTemporaryAlloca(b.dataPtrType, "")
if size < b.targetData.TypeAllocSize(b.i8ptrType) { if size < b.targetData.TypeAllocSize(b.dataPtrType) {
// The alloca is bigger than the value that will be stored in it. // The alloca is bigger than the value that will be stored in it.
// To avoid having some bits undefined, zero the alloca first. // To avoid having some bits undefined, zero the alloca first.
// Hopefully this will get optimized away. // Hopefully this will get optimized away.
b.CreateStore(llvm.ConstNull(b.i8ptrType), packedAlloc) b.CreateStore(llvm.ConstNull(b.dataPtrType), packedAlloc)
} }
// Store all values in the alloca. // Store all values in the alloca.
packedAllocCast := b.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
for i, value := range values { for i, value := range values {
indices := []llvm.Value{ indices := []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false), llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
} }
gep := b.CreateInBoundsGEP(packedType, packedAllocCast, indices, "") gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
b.CreateStore(value, gep) b.CreateStore(value, gep)
} }
// Load value (the *i8) from the alloca. // Load value (the *i8) from the alloca.
result := b.CreateLoad(b.i8ptrType, packedAlloc, "") result := b.CreateLoad(b.dataPtrType, packedAlloc, "")
// End the lifetime of the alloca, to help the optimizer. // End the lifetime of the alloca, to help the optimizer.
packedPtr := b.CreateBitCast(packedAlloc, b.i8ptrType, "")
packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false) packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false)
b.emitLifetimeEnd(packedPtr, packedSize) b.emitLifetimeEnd(packedAlloc, packedSize)
return result return result
} else { } else {
@@ -124,21 +122,20 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
global.SetLinkage(llvm.InternalLinkage) global.SetLinkage(llvm.InternalLinkage)
return llvm.ConstBitCast(global, b.i8ptrType) return global
} }
// Packed data is bigger than a pointer, so allocate it on the heap. // Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(b.uintptrType, size, false) sizeValue := llvm.ConstInt(b.uintptrType, size, false)
alloc := b.mod.NamedFunction("runtime.alloc") alloc := b.mod.NamedFunction("runtime.alloc")
packedHeapAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{ packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
sizeValue, sizeValue,
llvm.ConstNull(b.i8ptrType), llvm.ConstNull(b.dataPtrType),
llvm.Undef(b.i8ptrType), // unused context parameter llvm.Undef(b.dataPtrType), // unused context parameter
}, "") }, "")
if b.NeedsStackObjects { if b.NeedsStackObjects {
b.trackPointer(packedHeapAlloc) b.trackPointer(packedAlloc)
} }
packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
// Store all values in the heap pointer. // Store all values in the heap pointer.
for i, value := range values { for i, value := range values {
@@ -151,7 +148,7 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
} }
// Return the original heap allocation pointer, which already is an *i8. // Return the original heap allocation pointer, which already is an *i8.
return packedHeapAlloc return packedAlloc
} }
} }
@@ -160,28 +157,27 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll
packedType := b.ctx.StructType(valueTypes, false) packedType := b.ctx.StructType(valueTypes, false)
// Get a correctly-typed pointer to the packed data. // Get a correctly-typed pointer to the packed data.
var packedAlloc, packedRawAlloc llvm.Value var packedAlloc llvm.Value
needsLifetimeEnd := false
size := b.targetData.TypeAllocSize(packedType) size := b.targetData.TypeAllocSize(packedType)
if size == 0 { if size == 0 {
// No data to unpack. // No data to unpack.
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind { } else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
// A single pointer is always stored directly. // A single pointer is always stored directly.
return []llvm.Value{b.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")} return []llvm.Value{ptr}
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) { } else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
// Packed data stored directly in pointer. // Packed data stored directly in pointer.
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind { if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
// Keep this cast in SSA form. // Keep this cast in SSA form.
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")} return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
} }
// Fallback: load it using an alloca. // Fallback: load it using an alloca.
packedRawAlloc, _, _ = b.createTemporaryAlloca(llvm.PointerType(b.i8ptrType, 0), "unpack.raw.alloc") packedAlloc, _ = b.createTemporaryAlloca(b.dataPtrType, "unpack.raw.alloc")
packedRawValue := b.CreateBitCast(ptr, llvm.PointerType(b.i8ptrType, 0), "unpack.raw.value") b.CreateStore(ptr, packedAlloc)
b.CreateStore(packedRawValue, packedRawAlloc) needsLifetimeEnd = true
packedAlloc = b.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
} else { } else {
// Packed data stored on the heap. Bitcast the passed-in pointer to the // Packed data stored on the heap.
// correct pointer type. packedAlloc = ptr
packedAlloc = b.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
} }
// Load each value from the packed data. // Load each value from the packed data.
values := make([]llvm.Value, len(valueTypes)) values := make([]llvm.Value, len(valueTypes))
@@ -198,10 +194,9 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "") gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
values[i] = b.CreateLoad(valueType, gep, "") values[i] = b.CreateLoad(valueType, gep, "")
} }
if !packedRawAlloc.IsNil() { if needsLifetimeEnd {
allocPtr := b.CreateBitCast(packedRawAlloc, b.i8ptrType, "")
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false) allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
b.emitLifetimeEnd(allocPtr, allocSize) b.emitLifetimeEnd(packedAlloc, allocSize)
} }
return values return values
} }
@@ -253,12 +248,12 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// Do a few checks to see whether we need to generate any object layout // Do a few checks to see whether we need to generate any object layout
// information at all. // information at all.
objectSizeBytes := c.targetData.TypeAllocSize(t) objectSizeBytes := c.targetData.TypeAllocSize(t)
pointerSize := c.targetData.TypeAllocSize(c.i8ptrType) pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
pointerAlignment := c.targetData.PrefTypeAlignment(c.i8ptrType) pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
if objectSizeBytes < pointerSize { if objectSizeBytes < pointerSize {
// Too small to contain a pointer. // Too small to contain a pointer.
layout := (uint64(1) << 1) | 1 layout := (uint64(1) << 1) | 1
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
} }
bitmap := c.getPointerBitmap(t, pos) bitmap := c.getPointerBitmap(t, pos)
if bitmap.BitLen() == 0 { if bitmap.BitLen() == 0 {
@@ -266,13 +261,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// TODO: this can be done in many other cases, e.g. when allocating an // TODO: this can be done in many other cases, e.g. when allocating an
// array (like [4][]byte, which repeats a slice 4 times). // array (like [4][]byte, which repeats a slice 4 times).
layout := (uint64(1) << 1) | 1 layout := (uint64(1) << 1) | 1
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
} }
if objectSizeBytes%uint64(pointerAlignment) != 0 { if objectSizeBytes%uint64(pointerAlignment) != 0 {
// This shouldn't happen except for packed structs, which aren't // This shouldn't happen except for packed structs, which aren't
// currently used. // currently used.
c.addError(pos, "internal error: unexpected object size for object with pointer field") c.addError(pos, "internal error: unexpected object size for object with pointer field")
return llvm.ConstNull(c.i8ptrType) return llvm.ConstNull(c.dataPtrType)
} }
objectSizeWords := objectSizeBytes / uint64(pointerAlignment) objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
@@ -297,7 +292,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// The runtime knows that if the least significant bit of the pointer is // The runtime knows that if the least significant bit of the pointer is
// set, the pointer contains the value itself. // set, the pointer contains the value itself.
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1 layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
} }
// Unfortunately, the object layout is too big to fit in a pointer-sized // Unfortunately, the object layout is too big to fit in a pointer-sized
@@ -308,7 +303,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap) globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
global := c.mod.NamedGlobal(globalName) global := c.mod.NamedGlobal(globalName)
if !global.IsNil() { if !global.IsNil() {
return llvm.ConstBitCast(global, c.i8ptrType) return global
} }
// Create the global initializer. // Create the global initializer.
@@ -359,13 +354,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
global.AddMetadata(0, diglobal) global.AddMetadata(0, diglobal)
} }
return llvm.ConstBitCast(global, c.i8ptrType) return global
} }
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a // 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. // 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 { func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType) alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
switch typ.TypeKind() { switch typ.TypeKind() {
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind: case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
return big.NewInt(0) return big.NewInt(0)
@@ -378,7 +373,7 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
// of type uintptr, but before the LowerFuncValues pass it actually // of type uintptr, but before the LowerFuncValues pass it actually
// contains a pointer (ptrtoint) to a global. This trips up the // contains a pointer (ptrtoint) to a global. This trips up the
// interp package. Therefore, make the id field a pointer for now. // interp package. Therefore, make the id field a pointer for now.
typ = c.ctx.StructType([]llvm.Type{c.i8ptrType, c.i8ptrType}, false) typ = c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
} }
for i, subtyp := range typ.StructElementTypes() { for i, subtyp := range typ.StructElementTypes() {
subptrs := c.getPointerBitmap(subtyp, pos) subptrs := c.getPointerBitmap(subtyp, pos)
@@ -458,7 +453,7 @@ func (c *compilerContext) isThumb() bool {
func (b *builder) readStackPointer() llvm.Value { func (b *builder) readStackPointer() llvm.Value {
stacksave := b.mod.NamedFunction("llvm.stacksave") stacksave := b.mod.NamedFunction("llvm.stacksave")
if stacksave.IsNil() { if stacksave.IsNil() {
fnType := llvm.FunctionType(b.i8ptrType, nil, false) fnType := llvm.FunctionType(b.dataPtrType, nil, false)
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType) stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
} }
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "") return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
+13 -34
View File
@@ -8,22 +8,9 @@
package llvmutil package llvmutil
import ( import (
"strconv"
"strings"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// Major returns the LLVM major version.
func Major() int {
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil {
// sanity check, should be unreachable
panic("could not parse LLVM version: " + err.Error())
}
return llvmMajor
}
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though // CreateEntryBlockAlloca creates a new alloca in the entry block, even though
// the IR builder is located elsewhere. It assumes that the insert point is // the IR builder is located elsewhere. It assumes that the insert point is
// at the end of the current block. // at the end of the current block.
@@ -46,16 +33,14 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
// //
// This is useful for creating temporary allocas for intrinsics. Don't forget to // This is useful for creating temporary allocas for intrinsics. Don't forget to
// end the lifetime using emitLifetimeEnd after you're done with it. // end the lifetime using emitLifetimeEnd after you're done with it.
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, size llvm.Value) {
ctx := t.Context() ctx := t.Context()
targetData := llvm.NewTargetData(mod.DataLayout()) targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose() defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca = CreateEntryBlockAlloca(builder, t, name) alloca = CreateEntryBlockAlloca(builder, t, name)
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod) fnType, fn := getLifetimeStartFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "") builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
return return
} }
@@ -64,21 +49,19 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
ctx := mod.Context() ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout()) targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose() defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca := CreateEntryBlockAlloca(builder, t, name) alloca := CreateEntryBlockAlloca(builder, t, name)
builder.SetInsertPointBefore(inst) builder.SetInsertPointBefore(inst)
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod) fnType, fn := getLifetimeStartFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "") builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
if next := llvm.NextInstruction(inst); !next.IsNil() { if next := llvm.NextInstruction(inst); !next.IsNil() {
builder.SetInsertPointBefore(next) builder.SetInsertPointBefore(next)
} else { } else {
builder.SetInsertPointAtEnd(inst.InstructionParent()) builder.SetInsertPointAtEnd(inst.InstructionParent())
} }
fnType, fn = getLifetimeEndFunc(mod) fnType, fn = getLifetimeEndFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "") builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
return alloca return alloca
} }
@@ -94,13 +77,10 @@ func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value
// first if it doesn't exist yet. // first if it doesn't exist yet.
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) { func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
fnName := "llvm.lifetime.start.p0" fnName := "llvm.lifetime.start.p0"
if Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.lifetime.start.p0i8"
}
fn := mod.NamedFunction(fnName) fn := mod.NamedFunction(fnName)
ctx := mod.Context() ctx := mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false) fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
if fn.IsNil() { if fn.IsNil() {
fn = llvm.AddFunction(mod, fnName, fnType) fn = llvm.AddFunction(mod, fnName, fnType)
} }
@@ -111,13 +91,10 @@ func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
// first if it doesn't exist yet. // first if it doesn't exist yet.
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) { func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
fnName := "llvm.lifetime.end.p0" fnName := "llvm.lifetime.end.p0"
if Major() < 15 {
fnName = "llvm.lifetime.end.p0i8"
}
fn := mod.NamedFunction(fnName) fn := mod.NamedFunction(fnName)
ctx := mod.Context() ctx := mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false) fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
if fn.IsNil() { if fn.IsNil() {
fn = llvm.AddFunction(mod, fnName, fnType) fn = llvm.AddFunction(mod, fnName, fnType)
} }
@@ -213,13 +190,15 @@ func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
} }
// Add the new values. // Add the new values.
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
for _, value := range values { for _, value := range values {
usedValues = append(usedValues, llvm.ConstPointerCast(value, i8ptrType)) // Note: the bitcast is necessary to cast AVR function pointers to
// address space 0 pointer types.
usedValues = append(usedValues, llvm.ConstPointerCast(value, ptrType))
} }
// Create a new array (with the old and new values). // Create a new array (with the old and new values).
usedInitializer := llvm.ConstArray(i8ptrType, usedValues) usedInitializer := llvm.ConstArray(ptrType, usedValues)
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName) used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
used.SetInitializer(usedInitializer) used.SetInitializer(usedInitializer)
used.SetLinkage(llvm.AppendingLinkage) used.SetLinkage(llvm.AppendingLinkage)
+23 -30
View File
@@ -65,7 +65,7 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// Allocate the memory for the resulting type. Do not zero this memory: it // 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 // will be zeroed by the hashmap get implementation if the key is not
// present in the map. // present in the map.
mapValueAlloca, mapValuePtr, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value") mapValueAlloca, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
// We need the map size (with type uintptr) to pass to the hashmap*Get // 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 // functions. This is necessary because those *Get functions are valid on
@@ -82,19 +82,19 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
keyType = keyType.Underlying() keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string // key is a string
params := []llvm.Value{m, key, mapValuePtr, mapValueSize} params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "") commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
} else if hashmapIsBinaryKey(keyType) { } else if hashmapIsBinaryKey(keyType) {
// key can be compared with runtime.memequal // key can be compared with runtime.memequal
// Store the key in an alloca, in the entry block to avoid dynamic stack // Store the key in an alloca, in the entry block to avoid dynamic stack
// growth. // growth.
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, mapKeyAlloca) b.CreateStore(key, mapKeyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca) b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
// Fetch the value from the hashmap. // Fetch the value from the hashmap.
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize} params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "") commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
b.emitLifetimeEnd(mapKeyPtr, mapKeySize) b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
} else { } else {
// Not trivially comparable using memcmp. Make it an interface instead. // Not trivially comparable using memcmp. Make it an interface instead.
itfKey := key itfKey := key
@@ -102,14 +102,14 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// Not already an interface, so convert it to an interface now. // Not already an interface, so convert it to an interface now.
itfKey = b.createMakeInterface(key, origKeyType, pos) itfKey = b.createMakeInterface(key, origKeyType, pos)
} }
params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize} params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "") commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
} }
// Load the resulting value from the hashmap. The value is set to the zero // Load the resulting value from the hashmap. The value is set to the zero
// value if the key doesn't exist in the hashmap. // value if the key doesn't exist in the hashmap.
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "") mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize) b.emitLifetimeEnd(mapValueAlloca, mapValueAllocaSize)
if commaOk { if commaOk {
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false)) tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false))
@@ -124,22 +124,22 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// createMapUpdate updates a map key to a given value, by creating an // createMapUpdate updates a map key to a given value, by creating an
// appropriate runtime call. // appropriate runtime call.
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) { func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
valueAlloca, valuePtr, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value") valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
b.CreateStore(value, valueAlloca) b.CreateStore(value, valueAlloca)
origKeyType := keyType origKeyType := keyType
keyType = keyType.Underlying() keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string // key is a string
params := []llvm.Value{m, key, valuePtr} params := []llvm.Value{m, key, valueAlloca}
b.createRuntimeCall("hashmapStringSet", params, "") b.createRuntimeCall("hashmapStringSet", params, "")
} else if hashmapIsBinaryKey(keyType) { } else if hashmapIsBinaryKey(keyType) {
// key can be compared with runtime.memequal // key can be compared with runtime.memequal
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca) b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca) b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr, valuePtr} params := []llvm.Value{m, keyAlloca, valueAlloca}
b.createRuntimeCall("hashmapBinarySet", params, "") b.createRuntimeCall("hashmapBinarySet", params, "")
b.emitLifetimeEnd(keyPtr, keySize) b.emitLifetimeEnd(keyAlloca, keySize)
} else { } else {
// Key is not trivially comparable, so compare it as an interface instead. // Key is not trivially comparable, so compare it as an interface instead.
itfKey := key itfKey := key
@@ -147,10 +147,10 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
// Not already an interface, so convert it to an interface first. // Not already an interface, so convert it to an interface first.
itfKey = b.createMakeInterface(key, origKeyType, pos) itfKey = b.createMakeInterface(key, origKeyType, pos)
} }
params := []llvm.Value{m, itfKey, valuePtr} params := []llvm.Value{m, itfKey, valueAlloca}
b.createRuntimeCall("hashmapInterfaceSet", params, "") b.createRuntimeCall("hashmapInterfaceSet", params, "")
} }
b.emitLifetimeEnd(valuePtr, valueSize) b.emitLifetimeEnd(valueAlloca, valueSize)
} }
// createMapDelete deletes a key from a map by calling the appropriate runtime // createMapDelete deletes a key from a map by calling the appropriate runtime
@@ -164,12 +164,12 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
b.createRuntimeCall("hashmapStringDelete", params, "") b.createRuntimeCall("hashmapStringDelete", params, "")
return nil return nil
} else if hashmapIsBinaryKey(keyType) { } else if hashmapIsBinaryKey(keyType) {
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca) b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca) b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr} params := []llvm.Value{m, keyAlloca}
b.createRuntimeCall("hashmapBinaryDelete", params, "") b.createRuntimeCall("hashmapBinaryDelete", params, "")
b.emitLifetimeEnd(keyPtr, keySize) b.emitLifetimeEnd(keyAlloca, keySize)
return nil return nil
} else { } else {
// Key is not trivially comparable, so compare it as an interface // Key is not trivially comparable, so compare it as an interface
@@ -225,9 +225,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
} }
// Extract the key and value from the map. // Extract the key and value from the map.
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key") mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value") mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next") ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "") mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "") mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
@@ -238,8 +238,8 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
} }
// End the lifetimes of the allocas, because we're done with them. // End the lifetimes of the allocas, because we're done with them.
b.emitLifetimeEnd(mapKeyPtr, mapKeySize) b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
b.emitLifetimeEnd(mapValuePtr, mapValueSize) b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
// Construct the *ssa.Next return value: {ok, mapKey, mapValue} // Construct the *ssa.Next return value: {ok, mapKey, mapValue}
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false)) tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
@@ -333,14 +333,7 @@ func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
if fieldEndOffset != nextOffset { if fieldEndOffset != nextOffset {
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false) n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false) llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
gepPtr := elemPtr paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
if gepPtr.Type() != b.i8ptrType {
gepPtr = b.CreateBitCast(gepPtr, b.i8ptrType, "") // LLVM 14
}
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), gepPtr, []llvm.Value{llvmStoreSize}, "")
if paddingStart.Type() != b.i8ptrType {
paddingStart = b.CreateBitCast(paddingStart, b.i8ptrType, "") // LLVM 14
}
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "") b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
} }
} }
+18 -25
View File
@@ -96,7 +96,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// Add an extra parameter as the function context. This context is used in // Add an extra parameter as the function context. This context is used in
// closures and bound methods, but should be optimized away when not used. // closures and bound methods, but should be optimized away when not used.
if !info.exported { if !info.exported {
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", elemSize: 0}) paramInfos = append(paramInfos, paramInfo{llvmType: c.dataPtrType, name: "context", elemSize: 0})
} }
var paramTypes []llvm.Type var paramTypes []llvm.Type
@@ -145,20 +145,18 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
for _, attrName := range []string{"noalias", "nonnull"} { for _, attrName := range []string{"noalias", "nonnull"} {
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0)) llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
} }
if llvmutil.Major() >= 15 { // allockind etc are not available in LLVM 14 // Add attributes to signal to LLVM that this is an allocator function.
// Add attributes to signal to LLVM that this is an allocator // This enables a number of optimizations.
// function. This enables a number of optimizations. llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed))
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed)) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc"))
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc")) // Use a special value to indicate the first parameter:
// Use a special value to indicate the first parameter: // > allocsize has two integer arguments, but because they're both 32 bits, we can
// > allocsize has two integer arguments, but because they're both 32 bits, we can // > pack them into one 64-bit value, at the cost of making said value
// > pack them into one 64-bit value, at the cost of making said value // > nonsensical.
// > nonsensical. // >
// > // > In order to do this, we need to reserve one value of the second (optional)
// > In order to do this, we need to reserve one value of the second (optional) // > allocsize argument to signify "not present."
// > allocsize argument to signify "not present." llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff))
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff))
}
case "runtime.sliceAppend": case "runtime.sliceAppend":
// Appending a slice will only read the to-be-appended slice, it won't // Appending a slice will only read the to-be-appended slice, it won't
// be modified. // be modified.
@@ -180,7 +178,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
if strings.Split(c.Triple, "-")[0] == "avr" { if strings.Split(c.Triple, "-")[0] == "avr" {
// These functions are compiler-rt/libgcc functions that are // These functions are compiler-rt/libgcc functions that are
// currently implemented in Go. Assembly versions should appear in // currently implemented in Go. Assembly versions should appear in
// LLVM 16 hopefully. Until then, they need to be made available to // LLVM 17 hopefully. Until then, they need to be made available to
// the linker and the best way to do that is llvm.compiler.used. // the linker and the best way to do that is llvm.compiler.used.
// I considered adding a pragma for this, but the LLVM language // I considered adding a pragma for this, but the LLVM language
// reference explicitly says that this feature should not be exposed // reference explicitly says that this feature should not be exposed
@@ -445,15 +443,10 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0)) llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
if strings.Split(c.Triple, "-")[0] == "x86_64" { if strings.Split(c.Triple, "-")[0] == "x86_64" {
// Required by the ABI. // Required by the ABI.
if llvmutil.Major() < 15 { // The uwtable has two possible values: sync (1) or async (2). We use
// Needed for LLVM 14 support. // sync because we currently don't use async unwind tables.
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0)) // For details, see: https://llvm.org/docs/LangRef.html#function-attributes
} else { llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
// The uwtable has two possible values: sync (1) or async (2). We
// use sync because we currently don't use async unwind tables.
// For details, see: https://llvm.org/docs/LangRef.html#function-attributes
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
}
} }
} }
+1 -1
View File
@@ -183,7 +183,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
} }
llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false) llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false)
fn := b.getValue(call.Args[0], getPos(call)) fn := b.getValue(call.Args[0], getPos(call))
fnPtr := b.CreateIntToPtr(fn, llvm.PointerType(llvmType, 0), "") fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "")
// Prepare some functions that will be called later. // Prepare some functions that will be called later.
setLastError := b.mod.NamedFunction("SetLastError") setLastError := b.mod.NamedFunction("SetLastError")
+3 -12
View File
@@ -196,10 +196,6 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.foo(ptr %context) unnamed_addr #2 { define hidden void @main.foo(ptr %context) unnamed_addr #2 {
entry: entry:
%complit = alloca %main.kv.0, align 8
%stackalloc = alloca i8, align 1
store %main.kv.0 zeroinitializer, ptr %complit, align 8
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef) call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
ret void ret void
} }
@@ -207,15 +203,10 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 { define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
entry: entry:
%b1 = alloca %main.kv.0, align 8
%stackalloc = alloca i8, align 1
store %main.kv.0 zeroinitializer, ptr %b1, align 8
call void @runtime.trackPointer(ptr nonnull %b1, ptr nonnull %stackalloc, ptr undef) #3
store %main.kv.0 %b, ptr %b1, align 8
ret void ret void
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+6 -9
View File
@@ -31,12 +31,12 @@ entry:
ret void ret void
} }
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn ; 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) #3
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1 declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn ; 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) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
@@ -57,10 +57,7 @@ declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferen
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 { define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
entry: entry:
%complit = alloca {}, align 8
%chan.blockedList = alloca %runtime.channelBlockedList, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4 call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList) call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
@@ -110,8 +107,8 @@ select.body: ; preds = %select.next
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1 declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { argmemonly nocallback nofree nosync nounwind willreturn } attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #4 = { nounwind } attributes #4 = { nounwind }
+3 -3
View File
@@ -93,6 +93,6 @@ entry:
ret i8 %0 ret i8 %0
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
+3 -3
View File
@@ -44,7 +44,7 @@ entry:
ret void ret void
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+3 -3
View File
@@ -135,7 +135,7 @@ entry:
ret %runtime._interface %1 ret %runtime._interface %1
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+3 -3
View File
@@ -57,7 +57,7 @@ entry:
ret ptr %s.data ret ptr %s.data
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+11 -11
View File
@@ -138,7 +138,7 @@ entry:
ret void ret void
} }
; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly ; 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) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
@@ -156,24 +156,24 @@ entry:
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1 declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #4 declare i32 @llvm.smin.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.umin.i8(i8, i8) #4 declare i8 @llvm.umin.i8(i8, i8) #4
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #4 declare i32 @llvm.umin.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smax.i32(i32, i32) #4 declare i32 @llvm.smax.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umax.i32(i32, i32) #4 declare i32 @llvm.umax.i32(i32, i32) #4
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { argmemonly nocallback nofree nounwind willreturn writeonly } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #4 = { nocallback nofree nosync nounwind readnone speculatable willreturn } attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #5 = { nounwind } attributes #5 = { nounwind }
+14 -14
View File
@@ -21,7 +21,7 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr undef) #9 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
ret void ret void
} }
@@ -43,7 +43,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr undef) #9 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
ret void ret void
} }
@@ -76,7 +76,7 @@ entry:
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1 %1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
store ptr %n, ptr %1, align 4 store ptr %n, ptr %1, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #9 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
%2 = load i32, ptr %n, align 4 %2 = load i32, ptr %n, align 4
call void @runtime.printint32(i32 %2, ptr undef) #9 call void @runtime.printint32(i32 %2, ptr undef) #9
ret void ret void
@@ -113,7 +113,7 @@ entry:
store ptr %fn.context, ptr %1, align 4 store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2 %2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
store ptr %fn.funcptr, ptr %2, align 4 store ptr %fn.funcptr, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 16384, ptr undef) #9 call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 32768, ptr undef) #9
ret void ret void
} }
@@ -167,7 +167,7 @@ entry:
store i32 4, ptr %.repack1, align 4 store i32 4, ptr %.repack1, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2 %2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %2, align 4 store ptr %itf.typecode, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #9 call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
ret void ret void
} }
@@ -188,13 +188,13 @@ entry:
unreachable unreachable
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" } attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
attributes #4 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #6 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" } attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
attributes #7 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #8 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" } attributes #8 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #9 = { nounwind } attributes #9 = { nounwind }
+7 -7
View File
@@ -130,11 +130,11 @@ entry:
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6 declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" } attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
attributes #4 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" } attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
attributes #5 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" } attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #6 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" } attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #7 = { nounwind } attributes #7 = { nounwind }
+3 -3
View File
@@ -44,7 +44,7 @@ entry:
ret ptr %x ret ptr %x
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+10 -10
View File
@@ -72,13 +72,13 @@ entry:
declare void @main.undefinedFunctionNotInSection(ptr) #1 declare void @main.undefinedFunctionNotInSection(ptr) #1
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" } attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" } attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
attributes #7 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" } attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
attributes #8 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="foobar" "wasm-import-name"="imported" } attributes #8 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
attributes #9 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" } attributes #9 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" }
+3 -3
View File
@@ -322,7 +322,7 @@ unsafe.Slice.throw: ; preds = %entry
unreachable unreachable
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+3 -3
View File
@@ -97,7 +97,7 @@ lookup.throw: ; preds = %entry
unreachable unreachable
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+17 -49
View File
@@ -21,14 +21,9 @@ define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1
entry: entry:
%hashmap.key = alloca %main.hasPadding, align 8 %hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0 %0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1 %1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2 %2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #5
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value) call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key) call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 8 store %main.hasPadding %2, ptr %hashmap.key, align 8
@@ -43,14 +38,14 @@ entry:
ret i32 %6 ret i32 %6
} }
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4 declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4
declare void @runtime.memzero(ptr, i32, ptr) #1 declare void @runtime.memzero(ptr, i32, ptr) #1
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1 declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4 declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
@@ -58,14 +53,9 @@ define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b
entry: entry:
%hashmap.key = alloca %main.hasPadding, align 8 %hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0 %0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1 %1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2 %2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #5
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value) call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4 store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key) call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
@@ -87,24 +77,13 @@ define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2
entry: entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8 %hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #5
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value) call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key) call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0 %s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8 store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1 %hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1 %s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4 store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1 %0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5 call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9 %1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
@@ -125,25 +104,14 @@ define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2
entry: entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8 %hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #5
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value) call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4 store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key) call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0 %s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8 store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1 %hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1 %s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4 store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1 %0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5 call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9 %1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
@@ -164,9 +132,9 @@ entry:
ret void ret void
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #4 = { argmemonly nocallback nofree nosync nounwind willreturn } attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #5 = { nounwind } attributes #5 = { nounwind }
+1 -1
View File
@@ -16,7 +16,7 @@ I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help .PHONY: help
help: help:
@echo "Please use \`make <target>' where <target> is one of" @echo "Please use \`$(MAKE) <target>' where <target> is one of"
@echo " html to make standalone HTML files" @echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories" @echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file" @echo " singlehtml to make a single large HTML file"
+3 -3
View File
@@ -15,10 +15,10 @@ require (
github.com/mattn/go-tty v0.0.4 github.com/mattn/go-tty v0.0.4
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
go.bug.st/serial v1.6.0 go.bug.st/serial v1.6.0
golang.org/x/sys v0.4.0 golang.org/x/sys v0.11.0
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 golang.org/x/tools v0.12.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32 tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c
) )
require ( require (
+7 -9
View File
@@ -12,8 +12,6 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/deadprogram/go-serial v0.0.0-20230717164825-4529b3232919 h1:Hi7G1bCG70NwlyqGswJKEHoIi4hJVN1SsmfwZ+DQHtw=
github.com/deadprogram/go-serial v0.0.0-20230717164825-4529b3232919/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
@@ -50,7 +48,7 @@ github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A= go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -58,14 +56,14 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 h1:LHoToPgySGSr2NcUHbjENAidHz38RkKaNmmntwn9TjI= golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32 h1:LvdmoXncO43m2cws1chRB2hkLBAxfN6CbSjDI7+gk4Y= tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c h1:rS8mAFqf0CfxPCbtfsI3bWL4jkb0TBYA1wx7tY1nu28=
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0= tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+1 -1
View File
@@ -9,7 +9,7 @@ import (
// Version of TinyGo. // Version of TinyGo.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const Version = "0.29.0" const Version = "0.30.0"
var ( var (
// This variable is set at build time using -ldflags parameters. // This variable is set at build time using -ldflags parameters.
+4 -1
View File
@@ -49,7 +49,10 @@ func (inst *instruction) String() string {
operands[i] = op.String() operands[i] = op.String()
} }
name := instructionNameMap[inst.opcode] name := ""
if int(inst.opcode) < len(instructionNameMap) {
name = instructionNameMap[inst.opcode]
}
if name == "" { if name == "" {
name = "<unknown op>" name = "<unknown op>"
} }
+4 -4
View File
@@ -35,14 +35,14 @@ func isRecoverableError(err error) bool {
// ErrorLine is one line in a traceback. The position may be missing. // ErrorLine is one line in a traceback. The position may be missing.
type ErrorLine struct { type ErrorLine struct {
Pos token.Position Pos token.Position
Inst llvm.Value Inst string
} }
// Error encapsulates compile-time interpretation errors with an associated // Error encapsulates compile-time interpretation errors with an associated
// import path. The errors may not have a precise location attached. // import path. The errors may not have a precise location attached.
type Error struct { type Error struct {
ImportPath string ImportPath string
Inst llvm.Value Inst string
Pos token.Position Pos token.Position
Err error Err error
Traceback []ErrorLine Traceback []ErrorLine
@@ -60,10 +60,10 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
pos := getPosition(inst.llvmInst) pos := getPosition(inst.llvmInst)
return &Error{ return &Error{
ImportPath: r.pkgName, ImportPath: r.pkgName,
Inst: inst.llvmInst, Inst: inst.llvmInst.String(),
Pos: pos, Pos: pos,
Err: err, Err: err,
Traceback: []ErrorLine{{pos, inst.llvmInst}}, Traceback: []ErrorLine{{pos, inst.llvmInst.String()}},
} }
} }
+5 -6
View File
@@ -21,7 +21,7 @@ type runner struct {
targetData llvm.TargetData targetData llvm.TargetData
builder llvm.Builder builder llvm.Builder
pointerSize uint32 // cached pointer size from the TargetData pointerSize uint32 // cached pointer size from the TargetData
i8ptrType llvm.Type // often used type so created in advance dataPtrType llvm.Type // often used type so created in advance
uintptrType llvm.Type // equivalent to uintptr in Go uintptrType llvm.Type // equivalent to uintptr in Go
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
debug bool // log debug messages debug bool // log debug messages
@@ -46,9 +46,9 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
timeout: timeout, timeout: timeout,
} }
r.pointerSize = uint32(r.targetData.PointerSize()) r.pointerSize = uint32(r.targetData.PointerSize())
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0) r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0)
r.uintptrType = mod.Context().IntType(r.targetData.PointerSize() * 8) r.uintptrType = mod.Context().IntType(r.targetData.PointerSize() * 8)
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case) r.maxAlign = r.targetData.PrefTypeAlignment(r.dataPtrType) // assume pointers are maximally aligned (this is not always the case)
return &r return &r
} }
@@ -126,7 +126,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
mem.revert() mem.revert()
// Create a call to the package initializer (which was // Create a call to the package initializer (which was
// previously deleted). // previously deleted).
i8undef := llvm.Undef(r.i8ptrType) i8undef := llvm.Undef(r.dataPtrType)
r.builder.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{i8undef}, "") r.builder.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{i8undef}, "")
// Make sure that any globals touched by the package // Make sure that any globals touched by the package
// initializer, won't be accessed by later package initializers. // initializer, won't be accessed by later package initializers.
@@ -174,8 +174,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
newGlobal.SetLinkage(obj.llvmGlobal.Linkage()) newGlobal.SetLinkage(obj.llvmGlobal.Linkage())
newGlobal.SetAlignment(obj.llvmGlobal.Alignment()) newGlobal.SetAlignment(obj.llvmGlobal.Alignment())
// TODO: copy debug info, unnamed_addr, ... // TODO: copy debug info, unnamed_addr, ...
bitcast := llvm.ConstBitCast(newGlobal, obj.llvmGlobal.Type()) obj.llvmGlobal.ReplaceAllUsesWith(newGlobal)
obj.llvmGlobal.ReplaceAllUsesWith(bitcast)
name := obj.llvmGlobal.Name() name := obj.llvmGlobal.Name()
obj.llvmGlobal.EraseFromParentAsGlobal() obj.llvmGlobal.EraseFromParentAsGlobal()
newGlobal.SetName(name) newGlobal.SetName(name)
+6 -11
View File
@@ -57,16 +57,14 @@ func runTest(t *testing.T, pathPrefix string) {
if err != nil { if err != nil {
if err, match := err.(*Error); match { if err, match := err.(*Error); match {
println(err.Error()) println(err.Error())
if !err.Inst.IsNil() { if len(err.Inst) != 0 {
err.Inst.Dump() println(err.Inst)
println()
} }
if len(err.Traceback) > 0 { if len(err.Traceback) > 0 {
println("\ntraceback:") println("\ntraceback:")
for _, line := range err.Traceback { for _, line := range err.Traceback {
println(line.Pos.String() + ":") println(line.Pos.String() + ":")
line.Inst.Dump() println(line.Inst)
println()
} }
} }
} }
@@ -79,12 +77,9 @@ func runTest(t *testing.T, pathPrefix string) {
} }
// Run some cleanup passes to get easy-to-read outputs. // Run some cleanup passes to get easy-to-read outputs.
pm := llvm.NewPassManager() to := llvm.NewPassBuilderOptions()
defer pm.Dispose() defer to.Dispose()
pm.AddGlobalOptimizerPass() mod.RunPasses("globalopt,dse,adce", llvm.TargetMachine{}, to)
pm.AddDeadStoreEliminationPass()
pm.AddAggressiveDCEPass()
pm.Run(mod)
// Read the expected output IR. // Read the expected output IR.
out, err := os.ReadFile(pathPrefix + ".out.ll") out, err := os.ReadFile(pathPrefix + ".out.ll")
+15 -13
View File
@@ -338,6 +338,20 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
if mem.hasExternalStore(src) || mem.hasExternalLoadOrStore(dst) {
// These are the same checks as there are on llvm.Load
// and llvm.Store in the interpreter. Copying is
// essentially loading from the source array and storing
// to the destination array, hence why we need to do the
// same checks here.
// This fixes the following bug:
// https://github.com/tinygo-org/tinygo/issues/3890
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
nBytes := uint32(n * elemSize) nBytes := uint32(n * elemSize)
dstObj := mem.getWritable(dst.index()) dstObj := mem.getWritable(dst.index())
dstBuf := dstObj.buffer.asRawValue(r) dstBuf := dstObj.buffer.asRawValue(r)
@@ -529,7 +543,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// how this function got called. // how this function got called.
callErr.Traceback = append(callErr.Traceback, ErrorLine{ callErr.Traceback = append(callErr.Traceback, ErrorLine{
Pos: getPosition(inst.llvmInst), Pos: getPosition(inst.llvmInst),
Inst: inst.llvmInst, Inst: inst.llvmInst.String(),
}) })
return nil, mem, callErr return nil, mem, callErr
} }
@@ -1032,15 +1046,3 @@ func intPredicateString(predicate llvm.IntPredicate) string {
return "cmp?" return "cmp?"
} }
} }
// Strip some pointer casts. This is probably unnecessary once support for
// LLVM 14 (non-opaque pointers) is dropped.
func stripPointerCasts(value llvm.Value) llvm.Value {
if !value.IsAConstantExpr().IsNil() {
switch value.Opcode() {
case llvm.GetElementPtr, llvm.BitCast:
return stripPointerCasts(value.Operand(0))
}
}
return value
}
+4 -15
View File
@@ -658,20 +658,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
if v.offset() != 0 { if v.offset() != 0 {
// If there is an offset, make sure to use a GEP to index into the // If there is an offset, make sure to use a GEP to index into the
// pointer. // pointer.
// Cast to an i8* first (if needed) for easy indexing.
if llvmValue.Type() != mem.r.i8ptrType {
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
}
llvmValue = llvm.ConstInBoundsGEP(mem.r.mod.Context().Int8Type(), llvmValue, []llvm.Value{ llvmValue = llvm.ConstInBoundsGEP(mem.r.mod.Context().Int8Type(), llvmValue, []llvm.Value{
llvm.ConstInt(mem.r.mod.Context().Int32Type(), uint64(v.offset()), false), llvm.ConstInt(mem.r.mod.Context().Int32Type(), uint64(v.offset()), false),
}) })
} }
// If a particular LLVM pointer type is requested, cast to it.
if !llvmType.IsNil() && llvmType != llvmValue.Type() {
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
}
return llvmValue, nil return llvmValue, nil
} }
@@ -872,7 +863,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
if err != nil { if err != nil {
panic(err) panic(err)
} }
if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.i8ptrType) { if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.dataPtrType) {
// Probably trying to serialize a pointer to a byte array, // Probably trying to serialize a pointer to a byte array,
// perhaps as a result of rawLLVMValue() in a previous interp // perhaps as a result of rawLLVMValue() in a previous interp
// run. // run.
@@ -954,8 +945,6 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
// Because go-llvm doesn't have addrspacecast at the moment, // Because go-llvm doesn't have addrspacecast at the moment,
// do it indirectly with a ptrtoint/inttoptr pair. // do it indirectly with a ptrtoint/inttoptr pair.
llvmValue = llvm.ConstIntToPtr(llvm.ConstPtrToInt(llvmValue, mem.r.uintptrType), llvmType) llvmValue = llvm.ConstIntToPtr(llvm.ConstPtrToInt(llvmValue, mem.r.uintptrType), llvmType)
} else {
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
} }
} }
return llvmValue, nil return llvmValue, nil
@@ -1256,7 +1245,7 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
// For details on this format, see src/runtime/gc_precise.go. // For details on this format, see src/runtime/gc_precise.go.
func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) { func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
pointerSize := layoutValue.len(r) pointerSize := layoutValue.len(r)
if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.i8ptrType) { if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.dataPtrType) {
panic("inconsistent pointer size") panic("inconsistent pointer size")
} }
@@ -1331,12 +1320,12 @@ func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type {
// Create the LLVM type. // Create the LLVM type.
pointerSize := layoutValue.len(r) pointerSize := layoutValue.len(r)
pointerAlignment := r.targetData.PrefTypeAlignment(r.i8ptrType) pointerAlignment := r.targetData.PrefTypeAlignment(r.dataPtrType)
var fields []llvm.Type var fields []llvm.Type
for i := 0; i < int(objectSizeWords); { for i := 0; i < int(objectSizeWords); {
if bitmap.Bit(i) != 0 { if bitmap.Bit(i) != 0 {
// Pointer field. // Pointer field.
fields = append(fields, r.i8ptrType) fields = append(fields, r.dataPtrType)
i += int(pointerSize / uint32(pointerAlignment)) i += int(pointerSize / uint32(pointerAlignment))
} else { } else {
// Byte/word field. // Byte/word field.
+23
View File
@@ -7,6 +7,10 @@ target triple = "x86_64--linux"
@main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024] @main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024]
@main.int16SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.int16SliceSrc.buf, i64 3, i64 3 } @main.int16SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.int16SliceSrc.buf, i64 3, i64 3 }
@main.int16SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer @main.int16SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
@main.sliceSrcUntaint.buf = internal global [2 x i8] c"ab"
@main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
@@ -16,6 +20,8 @@ declare void @runtime.printuint8(i8)
declare void @runtime.printint16(i16) declare void @runtime.printint16(i16)
declare void @use(ptr)
define void @runtime.initAll() unnamed_addr { define void @runtime.initAll() unnamed_addr {
entry: entry:
call void @main.init() call void @main.init()
@@ -43,6 +49,15 @@ entry:
%int16SliceDst.buf = load ptr, ptr @main.int16SliceDst %int16SliceDst.buf = load ptr, ptr @main.int16SliceDst
%int16SliceDst.val = load i16, ptr %int16SliceDst.buf %int16SliceDst.val = load i16, ptr %int16SliceDst.buf
call void @runtime.printint16(i16 %int16SliceDst.val) call void @runtime.printint16(i16 %int16SliceDst.val)
; print(sliceDstUntaint[0])
%sliceDstUntaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstUntaint.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstUntaint.val)
; print(sliceDstTaint[0])
%sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstTaint.val)
ret void ret void
} }
@@ -79,5 +94,13 @@ entry:
%int16SliceSrc.buf = extractvalue { ptr, i64, i64 } %int16SliceSrc, 0 %int16SliceSrc.buf = extractvalue { ptr, i64, i64 } %int16SliceSrc, 0
%copy.n2 = call i64 @runtime.sliceCopy(ptr %int16SliceDst.buf, ptr %int16SliceSrc.buf, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2) %copy.n2 = call i64 @runtime.sliceCopy(ptr %int16SliceDst.buf, ptr %int16SliceSrc.buf, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2)
; Copy slice that has a known value.
%copy.n3 = call i64 @runtime.sliceCopy(ptr @main.sliceDstUntaint.buf, ptr @main.sliceSrcUntaint.buf, i64 2, i64 2, i64 1)
; Copy slice that might have been modified by the external @use call.
; This is a fix for https://github.com/tinygo-org/tinygo/issues/3890.
call void @use(ptr @main.sliceSrcTaint.buf)
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
ret void ret void
} }
+12
View File
@@ -1,12 +1,21 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux" target triple = "x86_64--linux"
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr
declare void @runtime.printuint8(i8) local_unnamed_addr declare void @runtime.printuint8(i8) local_unnamed_addr
declare void @runtime.printint16(i16) local_unnamed_addr declare void @runtime.printint16(i16) local_unnamed_addr
declare void @use(ptr) local_unnamed_addr
define void @runtime.initAll() unnamed_addr { define void @runtime.initAll() unnamed_addr {
entry: entry:
call void @use(ptr @main.sliceSrcTaint.buf)
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
ret void ret void
} }
@@ -16,5 +25,8 @@ entry:
call void @runtime.printuint8(i8 3) call void @runtime.printuint8(i8 3)
call void @runtime.printint16(i16 5) call void @runtime.printint16(i16 5)
call void @runtime.printint16(i16 5) call void @runtime.printint16(i16 5)
call void @runtime.printuint8(i8 97)
%sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1
call void @runtime.printuint8(i8 %sliceDstTaint.val)
ret void ret void
} }
+3 -5
View File
@@ -1289,16 +1289,14 @@ func printCompilerError(logln func(...interface{}), err error) {
case *interp.Error: case *interp.Error:
logln("#", err.ImportPath) logln("#", err.ImportPath)
logln(err.Error()) logln(err.Error())
if !err.Inst.IsNil() { if len(err.Inst) != 0 {
err.Inst.Dump() logln(err.Inst)
logln()
} }
if len(err.Traceback) > 0 { if len(err.Traceback) > 0 {
logln("\ntraceback:") logln("\ntraceback:")
for _, line := range err.Traceback { for _, line := range err.Traceback {
logln(line.Pos.String() + ":") logln(line.Pos.String() + ":")
line.Inst.Dump() logln(line.Inst)
logln()
} }
} }
case loader.Errors: case loader.Errors:
+25 -15
View File
@@ -10,47 +10,57 @@ import (
// Try it easily by opening the following site in Chrome. // Try it easily by opening the following site in Chrome.
// https://www.onlinemusictools.com/kb/ // https://www.onlinemusictools.com/kb/
const (
cable = 0
channel = 1
velocity = 0x40
)
func main() { func main() {
led := machine.LED led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput}) led.Configure(machine.PinConfig{Mode: machine.PinOutput})
button := machine.BUTTON button := machine.BUTTON
button.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
m := midi.Port() m := midi.Port()
m.SetHandler(func(b []byte) { m.SetRxHandler(func(b []byte) {
// blink when we receive a MIDI message
led.Set(!led.Get())
})
m.SetTxHandler(func() {
// blink when we send a MIDI message
led.Set(!led.Get()) led.Set(!led.Get())
m.Write(b)
}) })
prev := true prev := true
chords := []struct { chords := []struct {
name string name string
keys []midi.Note notes []midi.Note
}{ }{
{name: "C ", keys: []midi.Note{midi.C4, midi.E4, midi.G4}}, {name: "C ", notes: []midi.Note{midi.C4, midi.E4, midi.G4}},
{name: "G ", keys: []midi.Note{midi.G3, midi.B3, midi.D4}}, {name: "G ", notes: []midi.Note{midi.G3, midi.B3, midi.D4}},
{name: "Am", keys: []midi.Note{midi.A3, midi.C4, midi.E4}}, {name: "Am", notes: []midi.Note{midi.A3, midi.C4, midi.E4}},
{name: "F ", keys: []midi.Note{midi.F3, midi.A3, midi.C4}}, {name: "F ", notes: []midi.Note{midi.F3, midi.A3, midi.C4}},
} }
index := 0 index := 0
for { for {
current := button.Get() current := button.Get()
if prev != current { if prev != current {
led.Set(current)
if current { if current {
for _, c := range chords[index].keys { for _, note := range chords[index].notes {
m.NoteOff(0, 0, c, 0x40) m.NoteOff(cable, channel, note, velocity)
} }
index = (index + 1) % len(chords) index = (index + 1) % len(chords)
} else { } else {
for _, c := range chords[index].keys { for _, note := range chords[index].notes {
m.NoteOn(0, 0, c, 0x40) m.NoteOn(cable, channel, note, velocity)
} }
} }
prev = current prev = current
} }
time.Sleep(10 * time.Millisecond) time.Sleep(100 * time.Millisecond)
} }
} }
+96
View File
@@ -0,0 +1,96 @@
//go:build sam && atsamd21 && gemma_m0
package machine
// Used to reset into bootloader.
const resetMagicValue = 0xf01669ef
// GPIO Pins.
const (
D0 = PA04 // SERCOM0/PAD[0]
D1 = PA02
D2 = PA05 // SERCOM0/PAD[1]
D3 = PA00 // DotStar LED: SERCOM1/PAD[0]: APA102/MOSI
D4 = PA01 // DotStar LED: SERCOM1/PAD[1]: APA102/SCK
D11 = PA30 // Flash Access: SERCOM1/PAD[2]
D12 = PA31 // Flash Access: SERCOM1/PAD[3]
D13 = PA23 // LED: SERCOM3/PAD[1] SERCOM5/PAD[1]
)
// Analog pins.
const (
A0 = D1
A1 = D2
A2 = D0
)
const (
LED = PA23
)
// USBCDC pins.
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART0 pins.
const (
UART_TX_PIN = PA04 // TX: SERCOM0/PAD[0]
UART_RX_PIN = PA05 // RX: SERCOM0/PAD[1]
)
// UART0s on the Gemma M0.
var UART0 = &sercomUSART0
// SPI pins.
const (
SPI0_SCK_PIN = PA05 // SCK: SERCOM0/PAD[1]
SPI0_SDO_PIN = PA04 // MOSI: SERCOM0/PAD[0]
SPI0_SDI_PIN = NoPin
SPI0_CS_PIN = NoPin
)
// SPI on the Gemma M0.
var SPI0 = sercomSPIM0
// SPI pins for DotStar LED (using APA102 software SPI) and Flash.
const (
SPI1_SCK_PIN = PA01 // SCK: SERCOM1/PAD[0]
SPI1_SDO_PIN = PA00 // MOSI: SERCOM1/PAD[1]
SPI1_SDI_PIN = PA31 // MISO: SERCOM1/PAD[3]
SPI1_CS_PIN = PA30 // CS: SERCOM1/PAD[2]
)
// I2C pins.
const (
SDA_PIN = PA04 // SDA: SERCOM0/PAD[0]
SCL_PIN = PA05 // SCL: SERCOM0/PAD[1]
)
// I2C on the Gemma M0.
var (
I2C0 = sercomI2CM0
)
// I2S (not connected, needed for atsamd21).
const (
I2S_SCK_PIN = NoPin
I2S_SD_PIN = NoPin
I2S_WS_PIN = NoPin
)
// USB CDC identifiers.
const (
usb_STRING_PRODUCT = "Adafruit Gemma M0"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x801E
)
var (
DefaultUART = UART0
)
+17 -1
View File
@@ -225,7 +225,23 @@ func initEndpoint(ep, config uint32) {
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut: case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
// TODO: not really anything, seems like... // set packet size
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
// receive interrupts when current transfer complete
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
// set byte count to zero, we have not received anything yet
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// ready for next transfer
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn: case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
// set packet size // set packet size
+17 -1
View File
@@ -228,7 +228,23 @@ func initEndpoint(ep, config uint32) {
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut: case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
// TODO: not really anything, seems like... // set packet size
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
// receive interrupts when current transfer complete
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
// set byte count to zero, we have not received anything yet
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// ready for next transfer
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn: case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
// set packet size // set packet size
@@ -8,21 +8,21 @@ import (
) )
// https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/rp2040_usb_device_enumeration.c // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/rp2040_usb_device_enumeration.c
// According to errata RP2040-E5:
// "It is safe (and inexpensive) to enable the software workaround even when using versions of RP2040
// which include the fix in hardware."
// So let us always use the software fix.
func fixRP2040UsbDeviceEnumeration() { func fixRP2040UsbDeviceEnumeration() {
// After coming out of reset, the hardware expects 800us of LS_J (linestate J) time
// before it will move to the connected state. However on a hub that broadcasts packets
// for other devices this isn't the case. The plan here is to wait for the end of the bus
// reset, force an LS_J for 1ms and then switch control back to the USB phy. Unfortunately
// this requires us to use GPIO15 as there is no other way to force the input path.
// We only need to force DP as DM can be left at zero. It will be gated off by GPIO
// logic if it isn't func selected.
// Actually check for B0/B1 h/w // Wait SE0 phase will call force ls_j phase which will call finish phase
if ChipVersion() == 1 { hw_enumeration_fix_wait_se0()
// After coming out of reset, the hardware expects 800us of LS_J (linestate J) time
// before it will move to the connected state. However on a hub that broadcasts packets
// for other devices this isn't the case. The plan here is to wait for the end of the bus
// reset, force an LS_J for 1ms and then switch control back to the USB phy. Unfortunately
// this requires us to use GPIO15 as there is no other way to force the input path.
// We only need to force DP as DM can be left at zero. It will be gated off by GPIO
// logic if it isn't func selected.
// Wait SE0 phase will call force ls_j phase which will call finish phase
hw_enumeration_fix_wait_se0()
}
} }
func hw_enumeration_fix_wait_se0() { func hw_enumeration_fix_wait_se0() {
@@ -87,8 +87,8 @@ func hw_enumeration_fix_force_ls_j() {
// Switch to GPIO phy with LS_J forced // Switch to GPIO phy with LS_J forced
rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_DIGITAL_PAD | rp.USBCTRL_REGS_USB_MUXING_SOFTCON) rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_DIGITAL_PAD | rp.USBCTRL_REGS_USB_MUXING_SOFTCON)
// LS_J is now forced but while loop here just to check // LS_J is now forced but while loop to wait ~800us here just to check
waitCycles(125000) waitCycles(25000)
// if timer pool disabled, or no timer available, have to busy wait. // if timer pool disabled, or no timer available, have to busy wait.
hw_enumeration_fix_finish() hw_enumeration_fix_finish()
+226 -12
View File
@@ -1,19 +1,233 @@
package midi package midi
// NoteOn sends a note on message. import (
func (m *midi) NoteOn(cable, channel uint8, note Note, velocity uint8) { "errors"
m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|0x9, 0x90|(channel&0xf), byte(note)&0x7f, velocity&0x7f )
m.Write(m.msg[:])
// From USB-MIDI section 4.1 "Code Index Number (CIN) Classifications"
const (
CINSystemCommon2 = 0x2
CINSystemCommon3 = 0x3
CINSysExStart = 0x4
CINSysExEnd1 = 0x5
CINSysExEnd2 = 0x6
CINSysExEnd3 = 0x7
CINNoteOff = 0x8
CINNoteOn = 0x9
CINPoly = 0xA
CINControlChange = 0xB
CINProgramChange = 0xC
CINChannelPressure = 0xD
CINPitchBendChange = 0xE
CINSingleByte = 0xF
)
// Standard MIDI channel messages
const (
MsgNoteOff = 0x80
MsgNoteOn = 0x90
MsgPolyAftertouch = 0xA0
MsgControlChange = 0xB0
MsgProgramChange = 0xC0
MsgChannelAftertouch = 0xD0
MsgPitchBend = 0xE0
MsgSysExStart = 0xF0
MsgSysExEnd = 0xF7
)
// Standard MIDI control change messages
const (
CCModulationWheel = 0x01
CCBreathController = 0x02
CCFootPedal = 0x04
CCPortamentoTime = 0x05
CCDataEntry = 0x06
CCVolume = 0x07
CCBalance = 0x08
CCPan = 0x0A
CCExpression = 0x0B
CCEffectControl1 = 0x0C
CCEffectControl2 = 0x0D
CCGeneralPurpose1 = 0x10
CCGeneralPurpose2 = 0x11
CCGeneralPurpose3 = 0x12
CCGeneralPurpose4 = 0x13
CCBankSelect = 0x20
CCModulationDepthRange = 0x21
CCBreathControllerDepth = 0x22
CCFootPedalDepth = 0x24
CCEffectsLevel = 0x5B
CCTremeloLevel = 0x5C
CCChorusLevel = 0x5D
CCCelesteLevel = 0x5E
CCPhaserLevel = 0x5F
CCDataIncrement = 0x60
CCDataDecrement = 0x61
CCNRPNLSB = 0x62
CCNRPNMSB = 0x63
CCRPNLSB = 0x64
CCRPNMSB = 0x65
CCAllSoundOff = 0x78
CCResetAllControllers = 0x79
CCAllNotesOff = 0x7B
CCChannelVolume = 0x7F
)
var (
errInvalidMIDICable = errors.New("invalid MIDI cable")
errInvalidMIDIChannel = errors.New("invalid MIDI channel")
errInvalidMIDIVelocity = errors.New("invalid MIDI velocity")
errInvalidMIDIControl = errors.New("invalid MIDI control number")
errInvalidMIDIControlValue = errors.New("invalid MIDI control value")
errInvalidMIDIPatch = errors.New("invalid MIDI patch number")
errInvalidMIDIPitchBend = errors.New("invalid MIDI pitch bend value")
errInvalidMIDISysExData = errors.New("invalid MIDI SysEx data")
)
// NoteOn sends a channel note on message.
// The cable parameter is the cable number 0-15.
// The channel parameter is the MIDI channel number 1-16.
func (m *midi) NoteOn(cable, channel uint8, note Note, velocity uint8) error {
switch {
case cable > 15:
return errInvalidMIDICable
case channel == 0 || channel > 16:
return errInvalidMIDIChannel
case velocity > 127:
return errInvalidMIDIVelocity
}
m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINNoteOn, MsgNoteOn|(channel-1&0xf), byte(note)&0x7f, velocity&0x7f
_, err := m.Write(m.msg[:])
return err
} }
// NoteOff sends a note off message. // NoteOff sends a channel note off message.
func (m *midi) NoteOff(cable, channel uint8, note Note, velocity uint8) { // The cable parameter is the cable number 0-15.
m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|0x8, 0x80|(channel&0xf), byte(note)&0x7f, velocity&0x7f // The channel parameter is the MIDI channel number 1-16.
m.Write(m.msg[:]) func (m *midi) NoteOff(cable, channel uint8, note Note, velocity uint8) error {
switch {
case cable > 15:
return errInvalidMIDICable
case channel == 0 || channel > 16:
return errInvalidMIDIChannel
case velocity > 127:
return errInvalidMIDIVelocity
}
m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINNoteOff, MsgNoteOff|(channel-1&0xf), byte(note)&0x7f, velocity&0x7f
_, err := m.Write(m.msg[:])
return err
} }
// SendCC sends a continuous controller message. // ControlChange sends a channel continuous controller message.
func (m *midi) SendCC(cable, channel, control, value uint8) { // The cable parameter is the cable number 0-15.
m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|0xB, 0xB0|(channel&0xf), control&0x7f, value&0x7f // The channel parameter is the MIDI channel number 1-16.
m.Write(m.msg[:]) // The control parameter is the controller number 0-127.
// The value parameter is the controller value 0-127.
func (m *midi) ControlChange(cable, channel, control, value uint8) error {
switch {
case cable > 15:
return errInvalidMIDICable
case channel == 0 || channel > 16:
return errInvalidMIDIChannel
case control > 127:
return errInvalidMIDIControl
case value > 127:
return errInvalidMIDIControlValue
}
m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINControlChange, MsgControlChange|(channel-1&0xf), control&0x7f, value&0x7f
_, err := m.Write(m.msg[:])
return err
}
// ProgramChange sends a channel program change message.
// The cable parameter is the cable number 0-15.
// The channel parameter is the MIDI channel number 1-16.
// The patch parameter is the program number 0-127.
func (m *midi) ProgramChange(cable, channel uint8, patch uint8) error {
switch {
case cable > 15:
return errInvalidMIDICable
case channel == 0 || channel > 16:
return errInvalidMIDIChannel
case patch > 127:
return errInvalidMIDIPatch
}
m.msg[0], m.msg[1], m.msg[2] = (cable&0xf<<4)|CINProgramChange, MsgProgramChange|(channel-1&0xf), patch&0x7f
_, err := m.Write(m.msg[:3])
return err
}
// PitchBend sends a channel pitch bend message.
// The cable parameter is the cable number 0-15.
// The channel parameter is the MIDI channel number 1-16.
// The bend parameter is the 14-bit pitch bend value (maximum 0x3FFF).
// Setting bend above 0x2000 (up to 0x3FFF) will increase the pitch.
// Setting bend below 0x2000 (down to 0x0000) will decrease the pitch.
func (m *midi) PitchBend(cable, channel uint8, bend uint16) error {
switch {
case cable > 15:
return errInvalidMIDICable
case channel == 0 || channel > 16:
return errInvalidMIDIChannel
case bend > 0x3FFF:
return errInvalidMIDIPitchBend
}
m.msg[0], m.msg[1], m.msg[2], m.msg[3] = (cable&0xf<<4)|CINPitchBendChange, MsgPitchBend|(channel-1&0xf), byte(bend&0x7f), byte(bend>>8)&0x7f
_, err := m.Write(m.msg[:])
return err
}
// SysEx sends a System Exclusive message.
// The cable parameter is the cable number 0-15.
// The data parameter is a slice with the data to send.
// It needs to start with the manufacturer ID, which is either
// 1 or 3 bytes in length.
// The data slice should not include the SysEx start (0xF0) or
// end (0xF7) bytes, only the data in between.
func (m *midi) SysEx(cable uint8, data []byte) error {
switch {
case cable > 15:
return errInvalidMIDICable
case len(data) < 3:
return errInvalidMIDISysExData
}
// write start
m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExStart, MsgSysExStart
m.msg[2], m.msg[3] = data[0], data[1]
if _, err := m.Write(m.msg[:]); err != nil {
return err
}
// write middle
i := 2
for ; i < len(data)-2; i += 3 {
m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExStart, data[i]
m.msg[2], m.msg[3] = data[i+1], data[i+2]
if _, err := m.Write(m.msg[:]); err != nil {
return err
}
}
// write end
switch len(data) - i {
case 2:
m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExEnd3, data[i]
m.msg[2], m.msg[3] = data[i+1], MsgSysExEnd
case 1:
m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExEnd2, data[i]
m.msg[2], m.msg[3] = MsgSysExEnd, 0
case 0:
m.msg[0], m.msg[1] = (cable&0xf<<4)|CINSysExEnd1, MsgSysExEnd
m.msg[2], m.msg[3] = 0, 0
}
if _, err := m.Write(m.msg[:]); err != nil {
return err
}
return nil
} }
+27 -6
View File
@@ -17,6 +17,7 @@ type midi struct {
msg [4]byte msg [4]byte
buf *RingBuffer buf *RingBuffer
rxHandler func([]byte) rxHandler func([]byte)
txHandler func()
waitTxc bool waitTxc bool
} }
@@ -53,7 +54,7 @@ func newMidi() *midi {
Index: usb.MIDI_ENDPOINT_IN, Index: usb.MIDI_ENDPOINT_IN,
IsIn: true, IsIn: true,
Type: usb.ENDPOINT_TYPE_BULK, Type: usb.ENDPOINT_TYPE_BULK,
TxHandler: m.Handler, TxHandler: m.TxHandler,
}, },
}, },
[]usb.SetupConfig{}, []usb.SetupConfig{},
@@ -61,16 +62,32 @@ func newMidi() *midi {
return m return m
} }
// SetHandler is now deprecated, please use SetRxHandler().
func (m *midi) SetHandler(rxHandler func([]byte)) { func (m *midi) SetHandler(rxHandler func([]byte)) {
m.SetRxHandler(rxHandler)
}
// SetRxHandler sets the handler function for incoming MIDI messages.
func (m *midi) SetRxHandler(rxHandler func([]byte)) {
m.rxHandler = rxHandler m.rxHandler = rxHandler
} }
// SetTxHandler sets the handler function for outgoing MIDI messages.
func (m *midi) SetTxHandler(txHandler func()) {
m.txHandler = txHandler
}
func (m *midi) Write(b []byte) (n int, err error) { func (m *midi) Write(b []byte) (n int, err error) {
i := 0 s, e := 0, 0
for i = 0; i < len(b); i += 4 { for s = 0; s < len(b); s += 4 {
m.tx(b[i : i+4]) e = s + 4
if e > len(b) {
e = len(b)
}
m.tx(b[s:e])
} }
return i, nil return e, nil
} }
// sendUSBPacket sends a MIDIPacket. // sendUSBPacket sends a MIDIPacket.
@@ -79,7 +96,11 @@ func (m *midi) sendUSBPacket(b []byte) {
} }
// from BulkIn // from BulkIn
func (m *midi) Handler() { func (m *midi) TxHandler() {
if m.txHandler != nil {
m.txHandler()
}
m.waitTxc = false m.waitTxc = false
if b, ok := m.buf.Get(); ok { if b, ok := m.buf.Get(); ok {
m.waitTxc = true m.waitTxc = true
+2
View File
@@ -29,6 +29,7 @@
// ptrTo *typeStruct // ptrTo *typeStruct
// elem *typeStruct // element type of the array // elem *typeStruct // element type of the array
// arrayLen uintptr // length of the array (this is part of the type) // arrayLen uintptr // length of the array (this is part of the type)
// slicePtr *typeStruct // pointer to []T type
// - map types (this is still missing the key and element types) // - map types (this is still missing the key and element types)
// meta uint8 // meta uint8
// nmethods uint16 (0) // nmethods uint16 (0)
@@ -427,6 +428,7 @@ type arrayType struct {
ptrTo *rawType ptrTo *rawType
elem *rawType elem *rawType
arrayLen uintptr arrayLen uintptr
slicePtr *rawType
} }
type mapType struct { type mapType struct {
+39 -4
View File
@@ -555,8 +555,26 @@ func (v Value) Slice(i, j int) Value {
} }
case Array: case Array:
// TODO(dgryski): can't do this yet because the resulting value needs type slice of v.elem(), not array of v.elem(). v.checkAddressable()
// need to be able to look up this "new" type so pointer equality of types still works buf, length := buflen(v)
i, j := uintptr(i), uintptr(j)
if j < i || length < j {
slicePanic()
}
elemSize := v.typecode.underlying().elem().Size()
var hdr sliceHeader
hdr.len = j - i
hdr.cap = length - i
hdr.data = unsafe.Add(buf, i*elemSize)
sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr
return Value{
typecode: sliceType,
value: unsafe.Pointer(&hdr),
flags: v.flags,
}
case String: case String:
i, j := uintptr(i), uintptr(j) i, j := uintptr(i), uintptr(j)
@@ -604,9 +622,26 @@ func (v Value) Slice3(i, j, k int) Value {
} }
case Array: case Array:
// TODO(dgryski): can't do this yet because the resulting value needs type v.elem(), not array of v.elem(). v.checkAddressable()
// need to be able to look up this "new" type so pointer equality of types still works buf, length := buflen(v)
i, j, k := uintptr(i), uintptr(j), uintptr(k)
if j < i || k < j || length < k {
slicePanic()
}
elemSize := v.typecode.underlying().elem().Size()
var hdr sliceHeader
hdr.len = j - i
hdr.cap = k - i
hdr.data = unsafe.Add(buf, i*elemSize)
sliceType := (*arrayType)(unsafe.Pointer(v.typecode.underlying())).slicePtr
return Value{
typecode: sliceType,
value: unsafe.Pointer(&hdr),
flags: v.flags,
}
} }
panic("unimplemented: (reflect.Value).Slice3()") panic("unimplemented: (reflect.Value).Slice3()")
+1 -1
View File
@@ -420,7 +420,7 @@ func runGC() (freeBytes uintptr) {
// Mark phase: mark all reachable objects, recursively. // Mark phase: mark all reachable objects, recursively.
markStack() markStack()
markGlobals() findGlobals(markRoots)
if baremetal && hasScheduler { if baremetal && hasScheduler {
// Channel operations in interrupts may move task pointers around while we are marking. // Channel operations in interrupts may move task pointers around while we are marking.
+6 -5
View File
@@ -2,13 +2,14 @@
package runtime package runtime
// This file implements markGlobals for all the files that don't have a more // This file implements findGlobals for all systems where the start and end of
// specific implementation. // the globals section can be found through linker-defined symbols.
// markGlobals marks all globals, which are reachable by definition. // findGlobals finds all globals (which are reachable by definition) and calls
// the callback for them.
// //
// This implementation marks all globals conservatively and assumes it can use // This implementation marks all globals conservatively and assumes it can use
// linker-defined symbols for the start and end of the .data section. // linker-defined symbols for the start and end of the .data section.
func markGlobals() { func findGlobals(found func(start, end uintptr)) {
markRoots(globalsStart, globalsEnd) found(globalsStart, globalsEnd)
} }
-4
View File
@@ -100,7 +100,3 @@ func setHeapEnd(newHeapEnd uintptr) {
// enough. // enough.
heapEnd = newHeapEnd heapEnd = newHeapEnd
} }
func markRoots(start, end uintptr) {
// dummy, so that markGlobals will compile
}
-4
View File
@@ -37,7 +37,3 @@ func initHeap() {
func setHeapEnd(newHeapEnd uintptr) { func setHeapEnd(newHeapEnd uintptr) {
// Nothing to do here, this function is never actually called. // Nothing to do here, this function is never actually called.
} }
func markRoots(start, end uintptr) {
// dummy, so that markGlobals will compile
}
+3 -3
View File
@@ -54,12 +54,12 @@ type segmentLoadCommand struct {
//go:extern _mh_execute_header //go:extern _mh_execute_header
var libc_mh_execute_header machHeader var libc_mh_execute_header machHeader
// Mark global variables. // Find global variables in .data/.bss sections.
// The MachO linker doesn't seem to provide symbols for the start and end of the // The MachO linker doesn't seem to provide symbols for the start and end of the
// data section. There is get_etext, get_edata, and get_end, but these are // data section. There is get_etext, get_edata, and get_end, but these are
// undocumented and don't work with ASLR (which is enabled by default). // undocumented and don't work with ASLR (which is enabled by default).
// Therefore, read the MachO header directly. // Therefore, read the MachO header directly.
func markGlobals() { func findGlobals(found func(start, end uintptr)) {
// Here is a useful blog post to understand the MachO file format: // Here is a useful blog post to understand the MachO file format:
// https://h3adsh0tzz.com/2020/01/macho-file-format/ // https://h3adsh0tzz.com/2020/01/macho-file-format/
@@ -103,7 +103,7 @@ func markGlobals() {
// This could be improved by only reading the memory areas // This could be improved by only reading the memory areas
// covered by sections. That would reduce the amount of memory // covered by sections. That would reduce the amount of memory
// scanned a little bit (up to a single VM page). // scanned a little bit (up to a single VM page).
markRoots(offset+cmd.vmaddr, offset+cmd.vmaddr+cmd.vmsize) found(offset+cmd.vmaddr, offset+cmd.vmaddr+cmd.vmsize)
} }
} }
+4 -4
View File
@@ -77,9 +77,9 @@ type elfProgramHeader32 struct {
//go:extern __ehdr_start //go:extern __ehdr_start
var ehdr_start elfHeader var ehdr_start elfHeader
// markGlobals marks all globals, which are reachable by definition. // findGlobals finds globals in the .data/.bss sections.
// It parses the ELF program header to find writable segments. // It parses the ELF program header to find writable segments.
func markGlobals() { func findGlobals(found func(start, end uintptr)) {
// Relevant constants from the ELF specification. // Relevant constants from the ELF specification.
// See: https://refspecs.linuxfoundation.org/elf/elf.pdf // See: https://refspecs.linuxfoundation.org/elf/elf.pdf
const ( const (
@@ -99,14 +99,14 @@ func markGlobals() {
if header._type == PT_LOAD && header.flags&PF_W != 0 { if header._type == PT_LOAD && header.flags&PF_W != 0 {
start := header.vaddr start := header.vaddr
end := start + header.memsz end := start + header.memsz
markRoots(start, end) found(start, end)
} }
} else { } else {
header := (*elfProgramHeader32)(headerPtr) header := (*elfProgramHeader32)(headerPtr)
if header._type == PT_LOAD && header.flags&PF_W != 0 { if header._type == PT_LOAD && header.flags&PF_W != 0 {
start := header.vaddr start := header.vaddr
end := start + header.memsz end := start + header.memsz
markRoots(start, end) found(start, end)
} }
} }
headerPtr = unsafe.Add(headerPtr, ehdr_start.phentsize) headerPtr = unsafe.Add(headerPtr, ehdr_start.phentsize)
+2 -2
View File
@@ -52,7 +52,7 @@ var module *exeHeader
// around 160 bytes of amd64 instructions. // around 160 bytes of amd64 instructions.
// Most of this function is based on the documentation in // Most of this function is based on the documentation in
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format. // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format.
func markGlobals() { func findGlobals(found func(start, end uintptr)) {
// Constants used in this function. // Constants used in this function.
const ( const (
// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexa // https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexa
@@ -85,7 +85,7 @@ func markGlobals() {
// Found a writable section. Scan the entire section for roots. // Found a writable section. Scan the entire section for roots.
start := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) start := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress)
end := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) + uintptr(section.virtualSize) end := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) + uintptr(section.virtualSize)
markRoots(start, end) found(start, end)
} }
section = (*peSection)(unsafe.Add(unsafe.Pointer(section), unsafe.Sizeof(peSection{}))) section = (*peSection)(unsafe.Add(unsafe.Pointer(section), unsafe.Sizeof(peSection{})))
} }
+4 -4
View File
@@ -245,17 +245,17 @@ var bssStartSymbol [0]byte
//go:extern __bss_end //go:extern __bss_end
var bssEndSymbol [0]byte var bssEndSymbol [0]byte
// Mark global variables. // Find global variables.
// The linker script provides __*_start and __*_end symbols that can be used to // The linker script provides __*_start and __*_end symbols that can be used to
// scan the given sections. They are already aligned so don't need to be // scan the given sections. They are already aligned so don't need to be
// manually aligned here. // manually aligned here.
func markGlobals() { func findGlobals(found func(start, end uintptr)) {
dataStart := uintptr(unsafe.Pointer(&dataStartSymbol)) dataStart := uintptr(unsafe.Pointer(&dataStartSymbol))
dataEnd := uintptr(unsafe.Pointer(&dataEndSymbol)) dataEnd := uintptr(unsafe.Pointer(&dataEndSymbol))
markRoots(dataStart, dataEnd) found(dataStart, dataEnd)
bssStart := uintptr(unsafe.Pointer(&bssStartSymbol)) bssStart := uintptr(unsafe.Pointer(&bssStartSymbol))
bssEnd := uintptr(unsafe.Pointer(&bssEndSymbol)) bssEnd := uintptr(unsafe.Pointer(&bssEndSymbol))
markRoots(bssStart, bssEnd) found(bssStart, bssEnd)
} }
// getContextPtr returns the hblauncher context // getContextPtr returns the hblauncher context
-1
View File
@@ -1,4 +1,3 @@
; TODO: remove these in LLVM 15
#define __tmp_reg__ r16 #define __tmp_reg__ r16
#define __zero_reg__ r17 #define __zero_reg__ r17
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"inherits": ["riscv32"], "inherits": ["riscv32"],
"features": "+c,+m,-relax,-save-restore", "features": "+32bit,+c,+m,-64bit,-a,-d,-e,-experimental-zawrs,-experimental-zca,-experimental-zcd,-experimental-zcf,-experimental-zihintntl,-experimental-ztso,-experimental-zvfh,-f,-h,-relax,-save-restore,-svinval,-svnapot,-svpbmt,-v,-xtheadvdot,-xventanacondops,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zdinx,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zicbom,-zicbop,-zicboz,-zihintpause,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-zmmul,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b",
"build-tags": ["esp32c3", "esp"], "build-tags": ["esp32c3", "esp"],
"serial": "uart", "serial": "uart",
"rtlib": "compiler-rt", "rtlib": "compiler-rt",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"inherits": ["riscv32"], "inherits": ["riscv32"],
"cpu": "sifive-e31", "cpu": "sifive-e31",
"features": "+a,+c,+m,-64bit,-relax,-save-restore", "features": "+32bit,+a,+c,+m,-64bit,-d,-e,-experimental-zawrs,-experimental-zca,-experimental-zcd,-experimental-zcf,-experimental-zihintntl,-experimental-ztso,-experimental-zvfh,-f,-h,-relax,-save-restore,-svinval,-svnapot,-svpbmt,-v,-xtheadvdot,-xventanacondops,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zdinx,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zicbom,-zicbop,-zicboz,-zihintpause,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-zmmul,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b",
"build-tags": ["fe310", "sifive"] "build-tags": ["fe310", "sifive"]
} }
+10
View File
@@ -0,0 +1,10 @@
{
"inherits": ["atsamd21e18a"],
"build-tags": ["gemma_m0"],
"serial": "usb",
"serial-port": ["239a:801e"],
"flash-1200-bps-reset": "true",
"flash-method": "msd",
"msd-volume-name": ["GEMMABOOT"],
"msd-firmware-name": "firmware.uf2"
}
+3
View File
@@ -0,0 +1,3 @@
{
"inherits": ["pybadge"]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"inherits": ["riscv64"], "inherits": ["riscv64"],
"features": "+64bit,+a,+c,+d,+f,+m,-relax,-save-restore", "features": "+64bit,+a,+c,+d,+f,+m,-e,-experimental-zawrs,-experimental-zca,-experimental-zcd,-experimental-zcf,-experimental-zihintntl,-experimental-ztso,-experimental-zvfh,-h,-relax,-save-restore,-svinval,-svnapot,-svpbmt,-v,-xtheadvdot,-xventanacondops,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zdinx,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zicbom,-zicbop,-zicboz,-zihintpause,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-zmmul,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b",
"build-tags": ["k210", "kendryte"], "build-tags": ["k210", "kendryte"],
"code-model": "medium" "code-model": "medium"
} }
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"llvm-target": "aarch64", "llvm-target": "aarch64",
"cpu": "cortex-a57", "cpu": "cortex-a57",
"features": "+aes,+crc,+crypto,+fp-armv8,+neon,+sha2,+v8a", "features": "+aes,+crc,+crypto,+fp-armv8,+neon,+sha2,+v8a,-fmv",
"build-tags": ["nintendoswitch", "arm64"], "build-tags": ["nintendoswitch", "arm64"],
"scheduler": "tasks", "scheduler": "tasks",
"goos": "linux", "goos": "linux",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"inherits": ["riscv32"], "inherits": ["riscv32"],
"features": "+a,+c,+m,-relax,-save-restore", "features": "+32bit,+a,+c,+m,-64bit,-d,-e,-experimental-zawrs,-experimental-zca,-experimental-zcd,-experimental-zcf,-experimental-zihintntl,-experimental-ztso,-experimental-zvfh,-f,-h,-relax,-save-restore,-svinval,-svnapot,-svpbmt,-v,-xtheadvdot,-xventanacondops,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zdinx,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zicbom,-zicbop,-zicboz,-zihintpause,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-zmmul,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b",
"build-tags": ["virt", "qemu"], "build-tags": ["virt", "qemu"],
"default-stack-size": 4096, "default-stack-size": 4096,
"linkerscript": "targets/riscv-qemu.ld", "linkerscript": "targets/riscv-qemu.ld",
+1
View File
@@ -1,6 +1,7 @@
{ {
"inherits": ["riscv"], "inherits": ["riscv"],
"llvm-target": "riscv32-unknown-none", "llvm-target": "riscv32-unknown-none",
"cpu": "generic-rv32",
"target-abi": "ilp32", "target-abi": "ilp32",
"build-tags": ["tinygo.riscv32"], "build-tags": ["tinygo.riscv32"],
"scheduler": "tasks", "scheduler": "tasks",
+1
View File
@@ -1,6 +1,7 @@
{ {
"inherits": ["riscv"], "inherits": ["riscv"],
"llvm-target": "riscv64-unknown-none", "llvm-target": "riscv64-unknown-none",
"cpu": "generic-rv64",
"target-abi": "lp64", "target-abi": "lp64",
"build-tags": ["tinygo.riscv64"], "build-tags": ["tinygo.riscv64"],
"cflags": [ "cflags": [
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"llvm-target": "wasm32-unknown-wasi", "llvm-target": "wasm32-unknown-wasi",
"cpu": "generic", "cpu": "generic",
"features": "+bulk-memory,+nontrapping-fptoint,+sign-ext", "features": "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext",
"build-tags": ["tinygo.wasm", "wasi"], "build-tags": ["tinygo.wasm", "wasi"],
"goos": "linux", "goos": "linux",
"goarch": "arm", "goarch": "arm",
@@ -9,7 +9,7 @@
"libc": "wasi-libc", "libc": "wasi-libc",
"rtlib": "compiler-rt", "rtlib": "compiler-rt",
"scheduler": "asyncify", "scheduler": "asyncify",
"default-stack-size": 16384, "default-stack-size": 32768,
"cflags": [ "cflags": [
"-mbulk-memory", "-mbulk-memory",
"-mnontrapping-fptoint", "-mnontrapping-fptoint",

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