Compare commits

..

4 Commits

Author SHA1 Message Date
Jake Bailey 2761414777 compiler: make composite map hashes order dependent
Use the same multiply-and-XOR combination as the runtime interface hash for generated struct and array key hashes. This avoids systematic collisions when components are permuted.
2026-08-31 18:54:45 +02:00
Jake Bailey 7d4b42db83 compiler: test generated composite map hashes 2026-08-31 18:54:45 +02:00
Jake Bailey fc0673430d builder: add strlen to wasm builtins
LLVM 22 can replace string-scanning loops with calls to strlen during
optimization. Add the implementation to wasmbuiltins and invalidate
cached archives.

Add an optimized wasm-unknown smoke test that exercises the reflect
name-decoding path which exposed the missing symbol.
2026-08-30 20:39:59 +02:00
deadprogram 61c315cbfb ci: publish a draft release from the artifacts that CI already built
The Linux, macOS and Windows workflows build every file that a release
needs when the release branch is pushed. The new Release workflow finds
those runs for the tagged commit, waits for them, and collects their nine
files into a draft release. It builds nothing, so what ships is what was
tested.

The release notes come from the CHANGELOG.md entry for that version.

Signed-off-by: Ron Evans <ron@hybridgroup.com>
2026-08-30 10:13:28 +02:00
12 changed files with 425 additions and 1315 deletions
+35
View File
@@ -0,0 +1,35 @@
#!/bin/sh
# Print the CHANGELOG.md entry for one version, to use as release notes.
# The file uses a setext heading: the bare version, then a line of dashes.
set -e
version="$1"
if [ -z "$version" ]; then
echo "usage: $0 <version>" >&2
exit 1
fi
notes=$(awk -v version="$version" '
found {
if ($0 ~ /^---+$/ && previous != "") { previous = ""; exit }
if (previous != "") print previous
previous = $0
next
}
$0 ~ /^---+$/ && previous == version {
found = 1
previous = ""
next
}
{ previous = $0 }
END { if (found && previous != "") print previous }
' CHANGELOG.md)
if [ -z "$notes" ]; then
echo "no CHANGELOG.md entry for version $version" >&2
exit 1
fi
echo "$notes"
+158
View File
@@ -0,0 +1,158 @@
# Publish a GitHub release from the artifacts that CI already built.
#
# The Linux, macOS and Windows workflows build every file that a release needs
# when the release branch is pushed. This workflow collects the artifacts of
# those runs for the tagged commit, so what ships is what was tested.
#
# The release is a draft, so the notes can be reviewed before publication.
name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release, for example v0.42.0'
required: true
concurrency:
group: release-${{ inputs.tag || github.ref_name }}
cancel-in-progress: false
permissions:
contents: write
actions: read
jobs:
release:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
TAG: ${{ inputs.tag || github.ref_name }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ inputs.tag || github.ref_name }}
fetch-depth: 0
- name: Read the version
id: version
# The release file names come from goenv/version.go, not from the tag,
# so the two must agree.
run: |
version=$(./.github/workflows/tinygo-extract-version.sh | cut -d= -f2-)
case "$version" in
*-dev)
echo "::error::goenv/version.go has development version $version"
exit 1
;;
esac
if [ "v$version" != "$TAG" ]; then
echo "::error::tag $TAG does not match version $version in goenv/version.go"
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Wait for the CI runs of this commit
id: runs
# A pull request run has the merge commit as its head, so look at push
# runs only.
run: |
sha=$(git rev-parse HEAD)
for workflow in linux.yml build-macos.yml windows.yml; do
id=
for _ in $(seq 20); do
id=$(gh run list --workflow "$workflow" --commit "$sha" --event push --limit 1 --json databaseId --jq '.[0].databaseId')
if [ -n "$id" ]; then
break
fi
echo "waiting for $workflow to start on $sha"
sleep 30
done
if [ -z "$id" ]; then
echo "::error::no $workflow run for commit $sha"
exit 1
fi
echo "$workflow: run $id"
gh run watch "$id" --exit-status > /dev/null || true
conclusion=$(gh run view "$id" --json conclusion --jq .conclusion)
if [ "$conclusion" != "success" ]; then
echo "::error::$workflow run $id concluded with $conclusion"
exit 1
fi
case "$workflow" in
linux.yml) echo "linux=$id" >> "$GITHUB_OUTPUT" ;;
build-macos.yml) echo "macos=$id" >> "$GITHUB_OUTPUT" ;;
windows.yml) echo "windows=$id" >> "$GITHUB_OUTPUT" ;;
esac
done
# The build jobs upload with `archive: false`, so the artifact is the
# release file itself. skip-decompress keeps it that way. The Windows
# release is a .zip, which a download would otherwise unpack.
- name: Download the Linux artifacts
uses: actions/download-artifact@v8
with:
run-id: ${{ steps.runs.outputs.linux }}
github-token: ${{ github.token }}
path: artifacts
skip-decompress: true
- name: Download the macOS artifacts
uses: actions/download-artifact@v8
with:
run-id: ${{ steps.runs.outputs.macos }}
github-token: ${{ github.token }}
path: artifacts
skip-decompress: true
- name: Download the Windows artifacts
uses: actions/download-artifact@v8
with:
run-id: ${{ steps.runs.outputs.windows }}
github-token: ${{ github.token }}
path: artifacts
skip-decompress: true
- name: Collect the release files
# A build that stopped uploading must not give a half complete release,
# so list the files that are expected. The search is by file name, not
# by artifact name, because the artifact names are not uniform.
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
version=$VERSION
mkdir -p dist
missing=0
for file in \
"tinygo$version.linux-amd64.tar.gz" "tinygo_${version}_amd64.deb" \
"tinygo$version.linux-arm.tar.gz" "tinygo_${version}_armhf.deb" \
"tinygo$version.linux-arm64.tar.gz" "tinygo_${version}_arm64.deb" \
"tinygo$version.darwin-amd64.tar.gz" \
"tinygo$version.darwin-arm64.tar.gz" \
"tinygo$version.windows-amd64.zip"; do
found=$(find artifacts -type f -name "$file" | head -1)
if [ -z "$found" ]; then
echo "::error::missing release file $file"
missing=1
else
mv "$found" "dist/$file"
fi
done
ls -l dist
exit $missing
- name: Extract the release notes
env:
VERSION: ${{ steps.version.outputs.version }}
run: ./.github/workflows/extract-changelog.sh "$VERSION" > notes.md
- name: Create the draft release
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
version=$VERSION
set --
case "$version" in
*-*) set -- --prerelease ;;
esac
gh release create "$TAG" --draft --verify-tag "$@" \
--title "$version" \
--notes-file notes.md \
dist/*
+32
View File
@@ -123,3 +123,35 @@ the following command (for example in ~/lib):
TinyGo will get extracted to a `tinygo` directory. You can then call it with: TinyGo will get extracted to a `tinygo` directory. You can then call it with:
./tinygo/bin/tinygo ./tinygo/bin/tinygo
## Publish a release
The `Release` workflow (`.github/workflows/release.yml`) publishes releases. It
does not build anything. The Linux, macOS and Windows workflows already build
every file that a release needs when the `release` branch is pushed, so the
release workflow collects the artifacts of those runs for the tagged commit.
What ships is what was tested.
1. On the `dev` branch, set `const version` in `goenv/version.go` to the new
version (without a `v` prefix), and add the entry to `CHANGELOG.md`.
2. Merge `dev` into the `release` branch.
3. Tag that commit and push the tag:
git tag v0.42.0
git push origin v0.42.0
The tag must be `v` plus the version in `goenv/version.go`, because the
release file names come from that constant.
4. The workflow waits for the Linux, macOS and Windows runs of the tagged
commit, collects their nine files, and creates a **draft** release. The
release notes come from the `CHANGELOG.md` entry for that version.
5. Review the draft release and publish it.
6. On the `dev` branch, set `goenv/version.go` to the next `-dev` version.
To release again after a failure, delete the draft release and start the
workflow from the Actions tab with the tag as its input.
GitHub keeps a SHA-256 digest of every published file. The digest is not shown
on the release page, but it can be printed with:
gh release view v0.42.0 --json assets --jq '.assets[] | "\(.digest) \(.name)"'
+2 -2
View File
@@ -43,11 +43,11 @@ var libWasmBuiltins = Library{
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
librarySources: func(target string, _ bool) ([]string, error) { librarySources: func(target string, _ bool) ([]string, error) {
return []string{ return []string{
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and // Memory builtins needed for LLVM intrinsics and library calls.
// llvm.memset.* LLVM intrinsics.
"libc-top-half/musl/src/string/memcpy.c", "libc-top-half/musl/src/string/memcpy.c",
"libc-top-half/musl/src/string/memmove.c", "libc-top-half/musl/src/string/memmove.c",
"libc-top-half/musl/src/string/memset.c", "libc-top-half/musl/src/string/memset.c",
"libc-top-half/musl/src/string/strlen.c",
// exp, exp2, and log are needed for LLVM math builtin functions // exp, exp2, and log are needed for LLVM math builtin functions
// like llvm.exp.*. // like llvm.exp.*.
+3 -2
View File
@@ -23,8 +23,9 @@ import (
// builder.Library struct but that's hard to do since we want to know the // builder.Library struct but that's hard to do since we want to know the
// library path in advance in several places). // library path in advance in several places).
var libVersions = map[string]int{ var libVersions = map[string]int{
"musl": 3, "musl": 3,
"bdwgc": 2, "bdwgc": 2,
"wasmbuiltins": 1,
} }
// Config keeps all configuration affecting the build in a single struct. // Config keeps all configuration affecting the build in a single struct.
+5 -3
View File
@@ -417,6 +417,7 @@ func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, key
return b.createRuntimeCall("hashmapInterfacePtrHash", []llvm.Value{keyPtr, size, seed}, "hash") return b.createRuntimeCall("hashmapInterfacePtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
case *types.Struct: case *types.Struct:
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false) hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
multiplier := llvm.ConstInt(b.ctx.Int32Type(), 31, false)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < keyType.NumFields(); i++ { for i := 0; i < keyType.NumFields(); i++ {
if keyType.Field(i).Name() == "_" { if keyType.Field(i).Name() == "_" {
@@ -430,7 +431,7 @@ func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, key
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false) idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "") fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
fieldHash := b.generateKeyHash(fieldType, llvmFieldType, fieldPtr, seed) fieldHash := b.generateKeyHash(fieldType, llvmFieldType, fieldPtr, seed)
hash = b.CreateXor(hash, fieldHash, "") hash = b.CreateXor(b.CreateMul(hash, multiplier, ""), fieldHash, "")
} }
return hash return hash
case *types.Array: case *types.Array:
@@ -445,6 +446,7 @@ func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, key
if arrayLen == 0 { if arrayLen == 0 {
return llvm.ConstInt(b.ctx.Int32Type(), 0, false) return llvm.ConstInt(b.ctx.Int32Type(), 0, false)
} }
multiplier := llvm.ConstInt(b.ctx.Int32Type(), 31, false)
if arrayLen <= hashArrayUnrollLimit { if arrayLen <= hashArrayUnrollLimit {
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false) hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
@@ -452,7 +454,7 @@ func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, key
idx := llvm.ConstInt(b.uintptrType, uint64(i), false) idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "") elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed) elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
hash = b.CreateXor(hash, elemHash, "") hash = b.CreateXor(b.CreateMul(hash, multiplier, ""), elemHash, "")
} }
return hash return hash
} }
@@ -471,7 +473,7 @@ func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, key
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, phiI}, "") elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, phiI}, "")
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed) elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
newHash := b.CreateXor(phiHash, elemHash, "") newHash := b.CreateXor(b.CreateMul(phiHash, multiplier, ""), elemHash, "")
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "") nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
cond := b.CreateICmp(llvm.IntULT, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "") cond := b.CreateICmp(llvm.IntULT, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
b.CreateCondBr(cond, loopBody, loopDone) b.CreateCondBr(cond, loopBody, loopDone)
+20
View File
@@ -12,6 +12,11 @@ type nestedPadding struct {
i int i int
} }
type stringStruct struct {
a string
b string
}
//go:noinline //go:noinline
func testZeroGet(m map[hasPadding]int, s hasPadding) int { func testZeroGet(m map[hasPadding]int, s hasPadding) int {
return m[s] return m[s]
@@ -32,6 +37,21 @@ func testZeroArraySet(m map[[2]hasPadding]int, s [2]hasPadding) {
m[s] = 5 m[s] = 5
} }
//go:noinline
func makeStringStructMap() map[stringStruct]int {
return make(map[stringStruct]int)
}
//go:noinline
func makeShortStringArrayMap() map[[2]string]int {
return make(map[[2]string]int)
}
//go:noinline
func makeLongStringArrayMap() map[[5]string]int {
return make(map[[5]string]int)
}
func main() { func main() {
} }
+147
View File
@@ -4,6 +4,7 @@ target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-n
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%main.hasPadding = type { i1, i32, i1 } %main.hasPadding = type { i1, i32, i1 }
%runtime._string = type { ptr, i32 }
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
@@ -97,6 +98,152 @@ entry:
ret void ret void
} }
; Function Attrs: noinline nounwind
define hidden ptr @main.makeStringStructMap(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 16, i32 4, i32 8, ptr null, ptr nonnull @"hashmapKeyHash.struct{string; string}", ptr null, ptr nonnull @"hashmapKeyEqual.struct{string; string}", ptr undef) #4
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4
ret ptr %0
}
; Function Attrs: nounwind
define linkonce_odr i32 @"hashmapKeyHash.struct{string; string}"(ptr %0, i32 %1, i32 %2, ptr %3) unnamed_addr #1 {
entry:
%hash = call i32 @runtime.hashmapStringPtrHash(ptr %0, i32 8, i32 %2, ptr undef) #4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
%hash1 = call i32 @runtime.hashmapStringPtrHash(ptr nonnull %4, i32 8, i32 %2, ptr undef) #4
%5 = mul i32 %hash, 31
%6 = xor i32 %5, %hash1
ret i32 %6
}
declare i32 @runtime.hashmapStringPtrHash(ptr, i32, i32, ptr) #0
; Function Attrs: nounwind
define linkonce_odr i1 @"hashmapKeyEqual.struct{string; string}"(ptr %0, ptr %1, i32 %2, ptr %3) unnamed_addr #1 {
entry:
%x.str.unpack = load ptr, ptr %0, align 4
%x.str.elt4 = getelementptr inbounds nuw i8, ptr %0, i32 4
%x.str.unpack5 = load i32, ptr %x.str.elt4, align 4
%y.str.unpack = load ptr, ptr %1, align 4
%y.str.elt7 = getelementptr inbounds nuw i8, ptr %1, i32 4
%y.str.unpack8 = load i32, ptr %y.str.elt7, align 4
%eq = call i1 @runtime.stringEqual(ptr %x.str.unpack, i32 %x.str.unpack5, ptr %y.str.unpack, i32 %y.str.unpack8, ptr undef) #4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
%5 = getelementptr inbounds nuw i8, ptr %1, i32 8
%x.str1.unpack = load ptr, ptr %4, align 4
%x.str1.elt10 = getelementptr inbounds nuw i8, ptr %0, i32 12
%x.str1.unpack11 = load i32, ptr %x.str1.elt10, align 4
%y.str2.unpack = load ptr, ptr %5, align 4
%y.str2.elt13 = getelementptr inbounds nuw i8, ptr %1, i32 12
%y.str2.unpack14 = load i32, ptr %y.str2.elt13, align 4
%eq3 = call i1 @runtime.stringEqual(ptr %x.str1.unpack, i32 %x.str1.unpack11, ptr %y.str2.unpack, i32 %y.str2.unpack14, ptr undef) #4
%6 = and i1 %eq, %eq3
ret i1 %6
}
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0
declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr) #0
; Function Attrs: noinline nounwind
define hidden ptr @main.makeShortStringArrayMap(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 16, i32 4, i32 8, ptr null, ptr nonnull @"hashmapKeyHash.[2]string", ptr null, ptr nonnull @"hashmapKeyEqual.[2]string", ptr undef) #4
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4
ret ptr %0
}
; Function Attrs: nounwind
define linkonce_odr i32 @"hashmapKeyHash.[2]string"(ptr %0, i32 %1, i32 %2, ptr %3) unnamed_addr #1 {
entry:
%hash = call i32 @runtime.hashmapStringPtrHash(ptr %0, i32 8, i32 %2, ptr undef) #4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
%hash1 = call i32 @runtime.hashmapStringPtrHash(ptr nonnull %4, i32 8, i32 %2, ptr undef) #4
%5 = mul i32 %hash, 31
%6 = xor i32 %5, %hash1
ret i32 %6
}
; Function Attrs: nounwind
define linkonce_odr i1 @"hashmapKeyEqual.[2]string"(ptr %0, ptr %1, i32 %2, ptr %3) unnamed_addr #1 {
entry:
%x.str.unpack = load ptr, ptr %0, align 4
%x.str.elt4 = getelementptr inbounds nuw i8, ptr %0, i32 4
%x.str.unpack5 = load i32, ptr %x.str.elt4, align 4
%y.str.unpack = load ptr, ptr %1, align 4
%y.str.elt7 = getelementptr inbounds nuw i8, ptr %1, i32 4
%y.str.unpack8 = load i32, ptr %y.str.elt7, align 4
%eq = call i1 @runtime.stringEqual(ptr %x.str.unpack, i32 %x.str.unpack5, ptr %y.str.unpack, i32 %y.str.unpack8, ptr undef) #4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
%5 = getelementptr inbounds nuw i8, ptr %1, i32 8
%x.str1.unpack = load ptr, ptr %4, align 4
%x.str1.elt10 = getelementptr inbounds nuw i8, ptr %0, i32 12
%x.str1.unpack11 = load i32, ptr %x.str1.elt10, align 4
%y.str2.unpack = load ptr, ptr %5, align 4
%y.str2.elt13 = getelementptr inbounds nuw i8, ptr %1, i32 12
%y.str2.unpack14 = load i32, ptr %y.str2.elt13, align 4
%eq3 = call i1 @runtime.stringEqual(ptr %x.str1.unpack, i32 %x.str1.unpack11, ptr %y.str2.unpack, i32 %y.str2.unpack14, ptr undef) #4
%6 = and i1 %eq, %eq3
ret i1 %6
}
; Function Attrs: noinline nounwind
define hidden ptr @main.makeLongStringArrayMap(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 40, i32 4, i32 8, ptr null, ptr nonnull @"hashmapKeyHash.[5]string", ptr null, ptr nonnull @"hashmapKeyEqual.[5]string", ptr undef) #4
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4
ret ptr %0
}
; Function Attrs: nounwind
define linkonce_odr i32 @"hashmapKeyHash.[5]string"(ptr %0, i32 %1, i32 %2, ptr %3) unnamed_addr #1 {
entry:
br label %hash.array.body
hash.array.body: ; preds = %hash.array.body, %entry
%i = phi i32 [ 0, %entry ], [ %7, %hash.array.body ]
%hash.acc = phi i32 [ 0, %entry ], [ %6, %hash.array.body ]
%4 = getelementptr inbounds nuw %runtime._string, ptr %0, i32 %i
%hash = call i32 @runtime.hashmapStringPtrHash(ptr %4, i32 8, i32 %2, ptr undef) #4
%5 = mul i32 %hash.acc, 31
%6 = xor i32 %5, %hash
%7 = add nuw nsw i32 %i, 1
%8 = icmp samesign ult i32 %i, 4
br i1 %8, label %hash.array.body, label %hash.array.done
hash.array.done: ; preds = %hash.array.body
ret i32 %6
}
; Function Attrs: nounwind
define linkonce_odr i1 @"hashmapKeyEqual.[5]string"(ptr %0, ptr %1, i32 %2, ptr %3) unnamed_addr #1 {
entry:
br label %eq.array.body
eq.array.body: ; preds = %eq.array.body, %entry
%i = phi i32 [ 0, %entry ], [ %6, %eq.array.body ]
%4 = getelementptr inbounds %runtime._string, ptr %0, i32 %i
%5 = getelementptr inbounds %runtime._string, ptr %1, i32 %i
%x.str.unpack = load ptr, ptr %4, align 4
%x.str.elt1 = getelementptr inbounds nuw i8, ptr %4, i32 4
%x.str.unpack2 = load i32, ptr %x.str.elt1, align 4
%y.str.unpack = load ptr, ptr %5, align 4
%y.str.elt4 = getelementptr inbounds nuw i8, ptr %5, i32 4
%y.str.unpack5 = load i32, ptr %y.str.elt4, align 4
%eq = call i1 @runtime.stringEqual(ptr %x.str.unpack, i32 %x.str.unpack2, ptr %y.str.unpack, i32 %y.str.unpack5, ptr undef) #4
%6 = add i32 %i, 1
%7 = icmp ult i32 %6, 5
%.not7 = and i1 %7, %eq
br i1 %.not7, label %eq.array.body, label %eq.array.done
eq.array.done: ; preds = %eq.array.body
ret i1 %eq
}
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.main(ptr %context) unnamed_addr #1 { define hidden void @main.main(ptr %context) unnamed_addr #1 {
entry: entry:
+4 -3
View File
@@ -573,9 +573,10 @@ smoketest-riscv: | build/smoke
smoketest-wasm: SMOKE_OUT = build/smoke/wasm smoketest-wasm: SMOKE_OUT = build/smoke/wasm
smoketest-wasm: | build/smoke smoketest-wasm: | build/smoke
ifneq ($(WASM), 0) ifneq ($(WASM), 0)
$(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm examples/wasm/export $(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm examples/wasm/export
$(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm examples/wasm/main $(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm examples/wasm/main
$(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm-unknown examples/hello-wasm-unknown $(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm-unknown examples/hello-wasm-unknown
$(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm-unknown -opt=2 ./testdata/wasm-unknown-opt
endif endif
smoketest-flags: SMOKE_OUT = build/smoke/flags smoketest-flags: SMOKE_OUT = build/smoke/flags
+1 -2
View File
@@ -6,8 +6,7 @@
"features": "+32bit,+a,+c,+m,+zaamo,+zalrsc,+zmmul,-relax", "features": "+32bit,+a,+c,+m,+zaamo,+zalrsc,+zmmul,-relax",
"build-tags": [ "build-tags": [
"esp32c6", "esp32c6",
"esp", "esp"
"espradio"
], ],
"serial": "usb", "serial": "usb",
"rtlib": "compiler-rt", "rtlib": "compiler-rt",
+3 -1303
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
package main
import "reflect"
type value struct {
Field int
}
//go:wasmexport typeNameLength
func typeNameLength() uint32 {
return uint32(len(reflect.TypeOf(value{}).String()))
}
func main() {
}