mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-21 04:49:04 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 59b50926c7 | |||
| 93bc65479f | |||
| 1958d6ff9d | |||
| 1991b00395 | |||
| ada22bc691 | |||
| e3bf9306b3 | |||
| 6cd0c00060 | |||
| dc97300c2e | |||
| 6136fb7b95 | |||
| f21e027b5c | |||
| ac45c35924 | |||
| 338af91ae6 | |||
| d74f70bdd0 | |||
| 80506a9cea | |||
| c33682cf00 | |||
| 570a3deac2 | |||
| c4219439eb | |||
| 7a9c649268 | |||
| 213d10838f | |||
| b573ef3813 | |||
| 854f91ecf2 |
@@ -40,7 +40,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||||
run: go install -tags=llvm${{ matrix.version }}
|
run: go install -tags=llvm${{ matrix.version }}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Run go mod tidy
|
- name: Run go mod tidy
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
# statically linked binary.
|
# statically linked binary.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: golang:1.27rc2-alpine
|
image: golang:1.27-alpine
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
@@ -160,7 +160,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||||
@@ -196,6 +196,10 @@ jobs:
|
|||||||
cat /proc/cpuinfo
|
cat /proc/cpuinfo
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends \
|
sudo apt-get install --no-install-recommends \
|
||||||
|
libgcrypt20 \
|
||||||
|
libpixman-1-0 \
|
||||||
|
libsdl2-2.0-0 \
|
||||||
|
libslirp0 \
|
||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-system-riscv32 \
|
qemu-system-riscv32 \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
@@ -204,7 +208,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -216,6 +220,14 @@ jobs:
|
|||||||
version: "29.0.1"
|
version: "29.0.1"
|
||||||
- name: Setup `wasm-tools`
|
- name: Setup `wasm-tools`
|
||||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||||
|
- name: Install Espressif QEMU
|
||||||
|
run: |
|
||||||
|
release=esp-develop-9.2.2-20260417
|
||||||
|
archive=qemu-xtensa-softmmu-${release//-/_}-x86_64-linux-gnu.tar.xz
|
||||||
|
curl -fL --retry 3 -o "$RUNNER_TEMP/$archive" \
|
||||||
|
"https://github.com/espressif/qemu/releases/download/$release/$archive"
|
||||||
|
tar -xJf "$RUNNER_TEMP/$archive" -C "$RUNNER_TEMP"
|
||||||
|
echo "$RUNNER_TEMP/qemu/bin" >> "$GITHUB_PATH"
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
@@ -323,7 +335,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore cached LLVM source
|
- name: Restore cached LLVM source
|
||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v8
|
||||||
@@ -150,7 +150,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v8
|
||||||
@@ -176,7 +176,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.27.0-rc.2'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v8
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||||
FROM golang:1.27rc2 AS tinygo-llvm
|
FROM golang:1.27 AS tinygo-llvm
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
||||||
@@ -33,7 +33,7 @@ RUN cd /tinygo/ && \
|
|||||||
|
|
||||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||||
# all the build tools etc).
|
# all the build tools etc).
|
||||||
FROM golang:1.27rc2 AS tinygo-compiler
|
FROM golang:1.27 AS tinygo-compiler
|
||||||
|
|
||||||
# Copy tinygo build.
|
# Copy tinygo build.
|
||||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||||
|
|||||||
+23
-2
@@ -473,9 +473,15 @@ TEST_PACKAGES_NONWASM = \
|
|||||||
# (just like wasm).
|
# (just like wasm).
|
||||||
# * picolibc math functions apparently are less precise, the math package
|
# * picolibc math functions apparently are less precise, the math package
|
||||||
# fails on baremetal.
|
# fails on baremetal.
|
||||||
|
# * Since Go 1.27 the crypto tests below go through cryptotest.TestHash, which
|
||||||
|
# calls cryptotest.BoundarySlices. These targets report GOOS=linux, so they
|
||||||
|
# build boundary.go (//go:build linux || darwin) rather than
|
||||||
|
# boundary_compat.go, and that needs a working syscall.Mmap/syscall.Mprotect
|
||||||
|
# which we don't have. See #5593.
|
||||||
TEST_PACKAGES_BAREMETAL = $(filter-out $(TEST_PACKAGES_NONBAREMETAL), $(TEST_PACKAGES_FAST))
|
TEST_PACKAGES_BAREMETAL = $(filter-out $(TEST_PACKAGES_NONBAREMETAL), $(TEST_PACKAGES_FAST))
|
||||||
TEST_PACKAGES_NONBAREMETAL = \
|
TEST_PACKAGES_NONBAREMETAL = \
|
||||||
$(TEST_PACKAGES_NONWASM) \
|
$(TEST_PACKAGES_NONWASM) \
|
||||||
|
$(TEST_PACKAGES_NOBOUNDARYSLICES) \
|
||||||
math \
|
math \
|
||||||
$(nil)
|
$(nil)
|
||||||
|
|
||||||
@@ -484,6 +490,19 @@ TEST_PACKAGES_NOWASI = \
|
|||||||
crypto/ecdsa \
|
crypto/ecdsa \
|
||||||
$(nil)
|
$(nil)
|
||||||
|
|
||||||
|
# wasip1 reports GOOS=wasip1 and so gets the boundary_compat.go fallback, but
|
||||||
|
# wasip2 reports GOOS=linux and hits the same BoundarySlices problem as
|
||||||
|
# baremetal. On wasip2 syscall.Mmap returns ENOSYS and t.Fatalf cannot Goexit,
|
||||||
|
# so the test falls through and panics with "slice out of range".
|
||||||
|
TEST_PACKAGES_FAST_WASIP2 = $(filter-out $(TEST_PACKAGES_NOBOUNDARYSLICES), $(TEST_PACKAGES_FAST_WASI))
|
||||||
|
|
||||||
|
TEST_PACKAGES_NOBOUNDARYSLICES = \
|
||||||
|
crypto/md5 \
|
||||||
|
crypto/sha1 \
|
||||||
|
crypto/sha256 \
|
||||||
|
crypto/sha512 \
|
||||||
|
$(nil)
|
||||||
|
|
||||||
# Report platforms on which each standard library package is known to pass tests
|
# Report platforms on which each standard library package is known to pass tests
|
||||||
report-stdlib-tests-pass:
|
report-stdlib-tests-pass:
|
||||||
$(eval jointmp := $(shell echo /tmp/join.$$$$))
|
$(eval jointmp := $(shell echo /tmp/join.$$$$))
|
||||||
@@ -510,7 +529,7 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
|||||||
TEST_IOFS := false
|
TEST_IOFS := false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation'
|
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation|TestUnmarshalNestingLimitSlice|TestUnmarshalNestingLimitStruct'
|
||||||
TEST_ADDITIONAL_FLAGS ?=
|
TEST_ADDITIONAL_FLAGS ?=
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
@@ -519,6 +538,8 @@ TEST_ADDITIONAL_FLAGS ?=
|
|||||||
tinygo-test:
|
tinygo-test:
|
||||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||||
@# TestParseAndBytesRoundTrip/P256/Generic: needs Goexit to run defers on wasm.
|
@# TestParseAndBytesRoundTrip/P256/Generic: needs Goexit to run defers on wasm.
|
||||||
|
@# TestUnmarshalNestingLimit{Slice,Struct}: encoding/asn1 nesting limit added in
|
||||||
|
@# https://github.com/golang/go/commit/6a6d115f9a7422b2fa081ba6f567eefb4a099462
|
||||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml,$(TEST_PACKAGES_HOST)) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml,$(TEST_PACKAGES_HOST)) $(TEST_PACKAGES_SLOW)
|
||||||
ifeq ($(TEST_ENCODING_XML),true)
|
ifeq ($(TEST_ENCODING_XML),true)
|
||||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -stack-size=16MB encoding/xml
|
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -stack-size=16MB encoding/xml
|
||||||
@@ -549,7 +570,7 @@ tinygo-test-wasip1-fast:
|
|||||||
tinygo-test-wasip2-slow:
|
tinygo-test-wasip2-slow:
|
||||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW)
|
||||||
tinygo-test-wasip2-fast:
|
tinygo-test-wasip2-fast:
|
||||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASI) ./tests/runtime_wasi
|
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASIP2) ./tests/runtime_wasi
|
||||||
|
|
||||||
tinygo-test-wasip2-sum-slow:
|
tinygo-test-wasip2-sum-slow:
|
||||||
TINYGO=$(TINYGO) \
|
TINYGO=$(TINYGO) \
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
Use ASD-STE100 Simplified Technical English for content outside of the code like comments, PR description, PR title, github comments.
|
|
||||||
+9
-2
@@ -24,6 +24,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/gofrs/flock"
|
"github.com/gofrs/flock"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -269,6 +270,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||||
// program so it's pretty fast and doesn't need to be parallelized.
|
// program so it's pretty fast and doesn't need to be parallelized.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
|
buildProgram := sync.OnceFunc(program.Build)
|
||||||
|
|
||||||
// Add jobs to compile each package.
|
// Add jobs to compile each package.
|
||||||
// Packages that have a cache hit will not be compiled again.
|
// Packages that have a cache hit will not be compiled again.
|
||||||
@@ -398,8 +400,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile AST to IR. The compiler.CompilePackage function will
|
// SSA package builds may run concurrently, but the resulting
|
||||||
// build the SSA as needed.
|
// functions cannot be inspected until all builds have finished:
|
||||||
|
// generic instances and wrappers can be shared across packages.
|
||||||
|
// Build the whole program once before compiling any package.
|
||||||
|
buildProgram()
|
||||||
|
|
||||||
|
// Compile AST to IR.
|
||||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||||
defer mod.Context().Dispose()
|
defer mod.Context().Dispose()
|
||||||
defer mod.Dispose()
|
defer mod.Dispose()
|
||||||
|
|||||||
+6
-3
@@ -111,9 +111,6 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
|||||||
// Compute the size of the RAM portion of the image (everything the ROM
|
// Compute the size of the RAM portion of the image (everything the ROM
|
||||||
// bootloader loads, up to and including the appended SHA256 hash).
|
// bootloader loads, up to and including the appended SHA256 hash).
|
||||||
ramImageSize := 0
|
ramImageSize := 0
|
||||||
if makeImage {
|
|
||||||
ramImageSize += 4096
|
|
||||||
}
|
|
||||||
ramImageSize += 24 // image header (8) + trailer fields (16)
|
ramImageSize += 24 // image header (8) + trailer fields (16)
|
||||||
for _, seg := range segments {
|
for _, seg := range segments {
|
||||||
ramImageSize += 8 + len(seg.data) // segment header + data (4-aligned)
|
ramImageSize += 8 + len(seg.data) // segment header + data (4-aligned)
|
||||||
@@ -284,6 +281,9 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
|||||||
dromSize := 0
|
dromSize := 0
|
||||||
if len(dromSegs) > 0 {
|
if len(dromSegs) > 0 {
|
||||||
targetImageOffset := int(dromFlashAddr - flashBase)
|
targetImageOffset := int(dromFlashAddr - flashBase)
|
||||||
|
if makeImage {
|
||||||
|
targetImageOffset = int(dromFlashAddr)
|
||||||
|
}
|
||||||
if outf.Len() > targetImageOffset {
|
if outf.Len() > targetImageOffset {
|
||||||
return fmt.Errorf("ESP32: RAM segments too large (%d bytes), overlap DROM at flash 0x%x", outf.Len(), dromFlashAddr)
|
return fmt.Errorf("ESP32: RAM segments too large (%d bytes), overlap DROM at flash 0x%x", outf.Len(), dromFlashAddr)
|
||||||
}
|
}
|
||||||
@@ -304,6 +304,9 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
|||||||
}
|
}
|
||||||
iromFlashAddr := dromFlashAddr + uint32(dromPages)*pageSize
|
iromFlashAddr := dromFlashAddr + uint32(dromPages)*pageSize
|
||||||
targetImageOffset := int(iromFlashAddr - flashBase)
|
targetImageOffset := int(iromFlashAddr - flashBase)
|
||||||
|
if makeImage {
|
||||||
|
targetImageOffset = int(iromFlashAddr)
|
||||||
|
}
|
||||||
if outf.Len() > targetImageOffset {
|
if outf.Len() > targetImageOffset {
|
||||||
return fmt.Errorf("ESP32: DROM too large, overlaps IROM at flash 0x%x", iromFlashAddr)
|
return fmt.Errorf("ESP32: DROM too large, overlaps IROM at flash 0x%x", iromFlashAddr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,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", 4313, 323, 0, 2260},
|
{"hifive1b", "examples/echo", 4321, 323, 0, 2268},
|
||||||
{"microbit", "examples/serial", 2838, 382, 8, 2256},
|
{"microbit", "examples/serial", 2842, 382, 8, 2264},
|
||||||
{"wioterminal", "examples/pininterrupt", 8027, 1665, 132, 7488},
|
{"wioterminal", "examples/pininterrupt", 8039, 1665, 132, 7496},
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
@@ -296,6 +296,9 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CompilePackage compiles a single package to a LLVM module.
|
// CompilePackage compiles a single package to a LLVM module.
|
||||||
|
//
|
||||||
|
// The SSA package must already be built. When packages are compiled
|
||||||
|
// concurrently, the entire SSA program must be built before compilation starts.
|
||||||
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||||
defer c.dispose()
|
defer c.dispose()
|
||||||
@@ -306,9 +309,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||||
c.program = ssaPkg.Prog
|
c.program = ssaPkg.Prog
|
||||||
|
|
||||||
// Convert AST to SSA.
|
|
||||||
ssaPkg.Build()
|
|
||||||
|
|
||||||
// Assign names to function-local named types before compiling the
|
// Assign names to function-local named types before compiling the
|
||||||
// package, so that types declared in different functions (or in
|
// package, so that types declared in different functions (or in
|
||||||
// different instantiations of a generic function) do not collide.
|
// different instantiations of a generic function) do not collide.
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
// Update test if needed. Do not check the result.
|
// Update test if needed. Do not check the result.
|
||||||
if *flagUpdate {
|
if *flagUpdate {
|
||||||
err := os.WriteFile(outPath, []byte(mod.String()), 0666)
|
err := os.WriteFile(outPath, []byte(normalizeIR(mod.String())), 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to write updated output file:", err)
|
t.Error("failed to write updated output file:", err)
|
||||||
}
|
}
|
||||||
@@ -174,9 +174,8 @@ func TestOptimizedLargeAggregateABI(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalizeIR canonicalizes LLVM IR so a single golden file keeps matching
|
// normalizeIR canonicalizes LLVM-version-specific IR spellings for comparison
|
||||||
// across LLVM versions. Golden files are written against LLVM <21; newer LLVM
|
// and when regenerating golden files.
|
||||||
// prints some attributes differently.
|
|
||||||
func normalizeIR(s string) string {
|
func normalizeIR(s string) string {
|
||||||
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
||||||
// which LLVM printed before any co-occurring attribute such as
|
// which LLVM printed before any co-occurring attribute such as
|
||||||
@@ -414,5 +413,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string)
|
|||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
pkg := lprogram.MainPkg()
|
pkg := lprogram.MainPkg()
|
||||||
return CompilePackage(file, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
ssaPkg := program.Package(pkg.Pkg)
|
||||||
|
ssaPkg.Build()
|
||||||
|
return CompilePackage(file, pkg, ssaPkg, machine, compilerConfig, false)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+16
-16
@@ -19,33 +19,33 @@ define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %c
|
|||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value)
|
||||||
store i32 3, ptr %chan.value, align 4
|
store i32 3, ptr %chan.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
|
declare void @llvm.lifetime.start.p0(ptr nocapture) #2
|
||||||
|
|
||||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
|
declare void @llvm.lifetime.end.p0(ptr nocapture) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +55,9 @@ declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferen
|
|||||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +65,9 @@ entry:
|
|||||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ entry:
|
|||||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||||
%select.send.value = alloca i32, align 4
|
%select.send.value = alloca i32, align 4
|
||||||
store i32 1, ptr %select.send.value, align 4
|
store i32 1, ptr %select.send.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
|
call void @llvm.lifetime.start.p0(ptr nonnull %select.states.alloca)
|
||||||
store ptr %ch1, ptr %select.states.alloca, align 4
|
store ptr %ch1, ptr %select.states.alloca, align 4
|
||||||
%select.states.alloca.repack1 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 4
|
%select.states.alloca.repack1 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 4
|
||||||
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
||||||
@@ -86,7 +86,7 @@ entry:
|
|||||||
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
|
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
|
||||||
store ptr null, ptr %.repack3, align 4
|
store ptr null, ptr %.repack3, align 4
|
||||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #3
|
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
call void @llvm.lifetime.end.p0(ptr nonnull %select.states.alloca)
|
||||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||||
%2 = icmp eq i32 %1, 0
|
%2 = icmp eq i32 %1, 0
|
||||||
br i1 %2, label %select.done, label %select.next
|
br i1 %2, label %select.done, label %select.next
|
||||||
|
|||||||
Vendored
+5
-5
@@ -15,7 +15,7 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@"reflect/types.type:basic:string" = linkonce_odr constant { i8, ptr } { i8 81, ptr @"reflect/types.type:pointer:basic:string" }, align 4
|
@"reflect/types.type:basic:string" = linkonce_odr constant { i8, ptr } { i8 81, ptr @"reflect/types.type:pointer:basic:string" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:string" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:string" }, align 4
|
@"reflect/types.type:pointer:basic:string" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:string" }, align 4
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr readonly captures(none), ptr, ptr) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
@@ -31,7 +31,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.memcpy.p0.p0.i32(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i32, i1 immarg) #2
|
declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @"(main.largeReceiver).readLargeValue"(ptr readonly dereferenceable_or_null(1025) %receiver, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
define hidden i8 @"(main.largeReceiver).readLargeValue"(ptr readonly dereferenceable_or_null(1025) %receiver, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||||
@@ -344,7 +344,7 @@ if.then: ; preds = %typeassert.next
|
|||||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
|
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
|
||||||
declare void @llvm.memset.p0.i32(ptr writeonly captures(none), i8, i32, i1 immarg) #7
|
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #7
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.useLargeMap(ptr readonly dereferenceable_or_null(1025) %key, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
define hidden i8 @main.useLargeMap(ptr readonly dereferenceable_or_null(1025) %key, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
|
||||||
@@ -423,12 +423,12 @@ if.then: ; preds = %entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.start.p0(ptr captures(none)) #8
|
declare void @llvm.lifetime.start.p0(ptr nocapture) #8
|
||||||
|
|
||||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.end.p0(ptr captures(none)) #8
|
declare void @llvm.lifetime.end.p0(ptr nocapture) #8
|
||||||
|
|
||||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||||
|
|
||||||
|
|||||||
Vendored
+18
-18
@@ -21,23 +21,23 @@ entry:
|
|||||||
%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
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key)
|
||||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||||
%3 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
%3 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key)
|
||||||
%4 = load i32, ptr %hashmap.value, align 4
|
%4 = load i32, ptr %hashmap.value, align 4
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value)
|
||||||
ret i32 %4
|
ret i32 %4
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; 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(ptr nocapture) #3
|
||||||
|
|
||||||
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0
|
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; 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(ptr nocapture) #3
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
||||||
@@ -47,13 +47,13 @@ entry:
|
|||||||
%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
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(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(ptr nonnull %hashmap.key)
|
||||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||||
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,17 +64,17 @@ define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %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
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key)
|
||||||
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
||||||
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
||||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||||
%0 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
%0 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key)
|
||||||
%1 = load i32, ptr %hashmap.value, align 4
|
%1 = load i32, ptr %hashmap.value, align 4
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value)
|
||||||
ret i32 %1
|
ret i32 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,17 +83,17 @@ define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %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
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(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(ptr nonnull %hashmap.key)
|
||||||
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
||||||
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
||||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||||
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,8 +177,15 @@ func Build(pkgName, outpath string, config *compileopts.Config) error {
|
|||||||
// Pick a default output path based on the main directory.
|
// Pick a default output path based on the main directory.
|
||||||
outpath = filepath.Base(result.MainDir) + config.DefaultBinaryExtension()
|
outpath = filepath.Base(result.MainDir) + config.DefaultBinaryExtension()
|
||||||
}
|
}
|
||||||
|
} else if fi, statErr := os.Stat(outpath); statErr == nil && fi.IsDir() {
|
||||||
|
var name string
|
||||||
|
if strings.HasSuffix(pkgName, ".go") {
|
||||||
|
name = filepath.Base(pkgName[:len(pkgName)-3]) + config.DefaultBinaryExtension()
|
||||||
|
} else {
|
||||||
|
name = filepath.Base(result.MainDir) + config.DefaultBinaryExtension()
|
||||||
|
}
|
||||||
|
outpath = filepath.Join(outpath, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Rename(result.Binary, outpath); err != nil {
|
if err := os.Rename(result.Binary, outpath); err != nil {
|
||||||
// Moving failed. Do a file copy.
|
// Moving failed. Do a file copy.
|
||||||
inf, err := os.Open(result.Binary)
|
inf, err := os.Open(result.Binary)
|
||||||
|
|||||||
@@ -261,6 +261,21 @@ func TestTimerStopResetRace(t *testing.T) {
|
|||||||
runTest("timer_stop_reset_race.go", optionsFromTarget("", sema), t, nil, nil)
|
runTest("timer_stop_reset_race.go", optionsFromTarget("", sema), t, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestESP32QEMU(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
options := optionsFromTarget("esp32-qemu", sema)
|
||||||
|
emuCheck(t, options)
|
||||||
|
machines, err := exec.Command("qemu-system-xtensa", "-machine", "help").Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("failed to list qemu-system-xtensa machines:", err)
|
||||||
|
}
|
||||||
|
if !regexp.MustCompile(`(?m)^esp32\s`).Match(machines) {
|
||||||
|
t.Skip("qemu-system-xtensa does not support the ESP32 machine")
|
||||||
|
}
|
||||||
|
runTest("print.go", options, t, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||||
emuCheck(t, options)
|
emuCheck(t, options)
|
||||||
|
|
||||||
@@ -539,6 +554,9 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
if config.EmulatorName() == "simavr" {
|
if config.EmulatorName() == "simavr" {
|
||||||
actual = cleanSimAVRTestOutput(actual)
|
actual = cleanSimAVRTestOutput(actual)
|
||||||
}
|
}
|
||||||
|
if config.EmulatorName() == "qemu-system-xtensa" {
|
||||||
|
actual = cleanESP32QEMUOutput(actual)
|
||||||
|
}
|
||||||
if name == "testing.go" {
|
if name == "testing.go" {
|
||||||
// Strip actual time.
|
// Strip actual time.
|
||||||
re := regexp.MustCompile(`\([0-9]\.[0-9][0-9]s\)`)
|
re := regexp.MustCompile(`\([0-9]\.[0-9][0-9]s\)`)
|
||||||
@@ -564,6 +582,18 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanESP32QEMUOutput(output []byte) []byte {
|
||||||
|
entryLine := bytes.Index(output, []byte("\nentry "))
|
||||||
|
if entryLine < 0 {
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
entryLineEnd := bytes.IndexByte(output[entryLine+1:], '\n')
|
||||||
|
if entryLineEnd < 0 {
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
return output[entryLine+1+entryLineEnd+1:]
|
||||||
|
}
|
||||||
|
|
||||||
func cleanSimAVRTestOutput(output []byte) []byte {
|
func cleanSimAVRTestOutput(output []byte) []byte {
|
||||||
output = bytes.ReplaceAll(output, []byte{0x1b, '[', '3', '2', 'm'}, nil)
|
output = bytes.ReplaceAll(output, []byte{0x1b, '[', '3', '2', 'm'}, nil)
|
||||||
output = bytes.ReplaceAll(output, []byte{0x1b, '[', '0', 'm'}, nil)
|
output = bytes.ReplaceAll(output, []byte{0x1b, '[', '0', 'm'}, nil)
|
||||||
|
|||||||
@@ -68,14 +68,20 @@ tinygo_swapTask:
|
|||||||
|
|
||||||
// After the recursive spill returns, the physical register file still
|
// After the recursive spill returns, the physical register file still
|
||||||
// has WindowStart bits set for the spill helper frames.
|
// has WindowStart bits set for the spill helper frames.
|
||||||
// We will clear WindowStart completely (to 0) right before the retw.n
|
// We will drop every WindowStart bit except this window's own
|
||||||
// below, after the stack switch is done. This prevents stale overflow
|
// (WINDOWSTART = 1 << WINDOWBASE) right before the retw.n below, after the
|
||||||
// when the new goroutine's calls rotate back into these panes.
|
// stack switch is done. This prevents stale overflow when the new
|
||||||
|
// goroutine's calls rotate back into these panes.
|
||||||
|
|
||||||
// Restore interrupts.
|
// Reload the saved PS, but do NOT restore it yet: everything from the stack
|
||||||
|
// switch below until WindowStart is made consistent with the new stack must
|
||||||
|
// stay masked. Once sp points at the new task's stack, the memory below sp
|
||||||
|
// is that task's save area -- the a0-a3 the retw.n is about to reload
|
||||||
|
// through a window underflow. An interrupt landing in that gap builds its
|
||||||
|
// frame there and overwrites them, and a garbage a0 makes the next retw an
|
||||||
|
// Illegal Instruction. The PS restore below is therefore deferred to just
|
||||||
|
// before the retw.n, once the window state matches the new stack.
|
||||||
l32i a4, sp, 4 // reload saved PS
|
l32i a4, sp, 4 // reload saved PS
|
||||||
wsr.ps a4
|
|
||||||
rsync
|
|
||||||
|
|
||||||
// At this point, the following is true:
|
// At this point, the following is true:
|
||||||
// WindowStart == 1 << WindowBase
|
// WindowStart == 1 << WindowBase
|
||||||
@@ -97,16 +103,24 @@ tinygo_swapTask:
|
|||||||
// register also stores the parent register window.
|
// register also stores the parent register window.
|
||||||
l32i.n a0, sp, 0
|
l32i.n a0, sp, 0
|
||||||
|
|
||||||
// Clear ALL WindowStart bits. With all windows spilled to the stack,
|
// Drop every WindowStart bit except this window's. Stale bits (from the
|
||||||
// we must ensure no stale WS bits remain: the retw.n below will trigger
|
// spill helpers or the old goroutine) would overflow garbage registers into
|
||||||
// underflow4 to load the new goroutine's registers from the new stack
|
// memory. This window's own bit has to stay set: zeroing WindowStart marks
|
||||||
// (which sets the appropriate WS bit via rfwu). Any stale WS bits
|
// the running frame dead, so an interrupt arriving here returns through a
|
||||||
// (from spill helpers or the old goroutine) would cause spurious
|
// retw that underflows into a save area nothing ever wrote. Keeping it
|
||||||
// overflows of garbage register values into memory.
|
// costs nothing -- WindowStart[WindowBase - n] is still clear, so the
|
||||||
movi a5, 0
|
// retw.n below underflows as intended.
|
||||||
|
rsr a6, WINDOWBASE
|
||||||
|
movi a5, 1
|
||||||
|
ssl a6
|
||||||
|
sll a5, a5 // a5 = 1 << WindowBase
|
||||||
wsr a5, WINDOWSTART
|
wsr a5, WINDOWSTART
|
||||||
rsync
|
rsync
|
||||||
|
|
||||||
|
// Now that the new task's window state is consistent, take interrupts back.
|
||||||
|
wsr.ps a4
|
||||||
|
rsync
|
||||||
|
|
||||||
// Return into the new stack. This instruction will trigger a window
|
// Return into the new stack. This instruction will trigger a window
|
||||||
// underflow, reloading the saved registers from the stack.
|
// underflow, reloading the saved registers from the stack.
|
||||||
retw.n
|
retw.n
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||||
|
|
||||||
// attach
|
// attach
|
||||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
dev.Attach()
|
||||||
|
|
||||||
// enable interrupt for end of reset
|
// enable interrupt for end of reset
|
||||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||||
@@ -68,6 +68,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
dev.initcomplete = true
|
dev.initcomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach connects the device to the USB bus, allowing the host to detect and
|
||||||
|
// enumerate it. It can be used together with Detach to delay enumeration
|
||||||
|
// until the USB configuration (device identifiers, classes, ...) is complete.
|
||||||
|
func (dev *USBDevice) Attach() {
|
||||||
|
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach disconnects the device from the USB bus. To the host this appears
|
||||||
|
// as if the device was unplugged. A subsequent Attach makes the host
|
||||||
|
// enumerate the device again.
|
||||||
|
func (dev *USBDevice) Detach() {
|
||||||
|
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||||
|
}
|
||||||
|
|
||||||
func handlePadCalibration() {
|
func handlePadCalibration() {
|
||||||
// Load Pad Calibration data from non-volatile memory
|
// Load Pad Calibration data from non-volatile memory
|
||||||
// This requires registers that are not included in the SVD file.
|
// This requires registers that are not included in the SVD file.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||||
|
|
||||||
// attach
|
// attach
|
||||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
dev.Attach()
|
||||||
|
|
||||||
// enable interrupt for end of reset
|
// enable interrupt for end of reset
|
||||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||||
@@ -71,6 +71,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
dev.initcomplete = true
|
dev.initcomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach connects the device to the USB bus, allowing the host to detect and
|
||||||
|
// enumerate it. It can be used together with Detach to delay enumeration
|
||||||
|
// until the USB configuration (device identifiers, classes, ...) is complete.
|
||||||
|
func (dev *USBDevice) Attach() {
|
||||||
|
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach disconnects the device from the USB bus. To the host this appears
|
||||||
|
// as if the device was unplugged. A subsequent Attach makes the host
|
||||||
|
// enumerate the device again.
|
||||||
|
func (dev *USBDevice) Detach() {
|
||||||
|
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||||
|
}
|
||||||
|
|
||||||
func handlePadCalibration() {
|
func handlePadCalibration() {
|
||||||
// Load Pad Calibration data from non-volatile memory
|
// Load Pad Calibration data from non-volatile memory
|
||||||
// This requires registers that are not included in the SVD file.
|
// This requires registers that are not included in the SVD file.
|
||||||
|
|||||||
@@ -112,6 +112,11 @@ func (p Pin) configure(config PinConfig, signal uint32) {
|
|||||||
// Configure the pad with the given IO mux configuration.
|
// Configure the pad with the given IO mux configuration.
|
||||||
p.mux().Set(muxConfig)
|
p.mux().Set(muxConfig)
|
||||||
|
|
||||||
|
// Internal pull resistors for pins with RTC function ignore
|
||||||
|
// the IO_MUX_GPIO0_FUN_WPU and IO_MUX_GPIO0_FUN_WPD bits set
|
||||||
|
// above and are instead controlled by the RTC_IO registers.
|
||||||
|
p.configureRTCPull(config.Mode)
|
||||||
|
|
||||||
switch config.Mode {
|
switch config.Mode {
|
||||||
case PinOutput:
|
case PinOutput:
|
||||||
// Set the 'output enable' bit.
|
// Set the 'output enable' bit.
|
||||||
@@ -293,6 +298,60 @@ func (p Pin) mux() *volatile.Register32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configureRTCPull applies the pullup/pulldown setting to a pin.
|
||||||
|
// This mirrors ESP-IDF:
|
||||||
|
// https://github.com/espressif/esp-idf/blob/08e0d30/components/esp_driver_gpio/src/gpio.c#L283
|
||||||
|
// https://github.com/espressif/esp-idf/blob/08e0d30/components/esp_hal_gpio/esp32/rtc_io_periph.c#L57
|
||||||
|
// https://github.com/espressif/esp-idf/blob/08e0d30/components/esp_hal_gpio/esp32/include/hal/rtc_io_ll.h#L175
|
||||||
|
func (p Pin) configureRTCPull(mode PinMode) {
|
||||||
|
var rue, rde uint32
|
||||||
|
switch mode {
|
||||||
|
case PinInputPullup:
|
||||||
|
rue = 1
|
||||||
|
case PinInputPulldown:
|
||||||
|
rde = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p {
|
||||||
|
case 0:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD1_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD1_RDE(rde)
|
||||||
|
case 2:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD2_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD2_RDE(rde)
|
||||||
|
case 4:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD0_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD0_RDE(rde)
|
||||||
|
case 12:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD5_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD5_RDE(rde)
|
||||||
|
case 13:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD4_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD4_RDE(rde)
|
||||||
|
case 14:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD6_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD6_RDE(rde)
|
||||||
|
case 15:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD3_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD3_RDE(rde)
|
||||||
|
case 25:
|
||||||
|
esp.RTC_IO.SetPAD_DAC1_PDAC1_RUE(rue)
|
||||||
|
esp.RTC_IO.SetPAD_DAC1_PDAC1_RDE(rde)
|
||||||
|
case 26:
|
||||||
|
esp.RTC_IO.SetPAD_DAC2_PDAC2_RUE(rue)
|
||||||
|
esp.RTC_IO.SetPAD_DAC2_PDAC2_RDE(rde)
|
||||||
|
case 27:
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD7_RUE(rue)
|
||||||
|
esp.RTC_IO.SetTOUCH_PAD7_RDE(rde)
|
||||||
|
case 32:
|
||||||
|
esp.RTC_IO.SetXTAL_32K_PAD_X32P_RUE(rue)
|
||||||
|
esp.RTC_IO.SetXTAL_32K_PAD_X32P_RDE(rde)
|
||||||
|
case 33:
|
||||||
|
esp.RTC_IO.SetXTAL_32K_PAD_X32N_RUE(rue)
|
||||||
|
esp.RTC_IO.SetXTAL_32K_PAD_X32N_RDE(rde)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const maxPin = 40
|
const maxPin = 40
|
||||||
|
|
||||||
// cpuInterruptFromPin selects an edge-triggered CPU interrupt line for GPIO.
|
// cpuInterruptFromPin selects an edge-triggered CPU interrupt line for GPIO.
|
||||||
|
|||||||
@@ -242,7 +242,6 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error {
|
|||||||
timeoutNS := int64(timeoutMS) * 1000000
|
timeoutNS := int64(timeoutMS) * 1000000
|
||||||
needAddress := true
|
needAddress := true
|
||||||
needRestart := false
|
needRestart := false
|
||||||
readLast := false
|
|
||||||
var readTo []byte
|
var readTo []byte
|
||||||
for cmdIdx, reg := 0, &i2c.Bus.COMD0; cmdIdx < len(cmd); {
|
for cmdIdx, reg := 0, &i2c.Bus.COMD0; cmdIdx < len(cmd); {
|
||||||
c := &cmd[cmdIdx]
|
c := &cmd[cmdIdx]
|
||||||
@@ -310,7 +309,6 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if split {
|
if split {
|
||||||
readLast = true
|
|
||||||
reg.Set(i2cCMD_READLAST | 1)
|
reg.Set(i2cCMD_READLAST | 1)
|
||||||
reg = nextAddress(reg)
|
reg = nextAddress(reg)
|
||||||
readTo = c.data[c.head : c.head+bytes+1] // read bytes + 1 last byte
|
readTo = c.data[c.head : c.head+bytes+1] // read bytes + 1 last byte
|
||||||
@@ -342,7 +340,7 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case mask&esp.I2C_INT_STATUS_ACK_ERR_INT_ST_Msk != 0 && !readLast:
|
case mask&esp.I2C_INT_STATUS_ACK_ERR_INT_ST_Msk != 0:
|
||||||
return errI2CAckExpected
|
return errI2CAckExpected
|
||||||
case mask&esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk != 0:
|
case mask&esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk != 0:
|
||||||
// timeout leaves the bus in an undefined state, reset
|
// timeout leaves the bus in an undefined state, reset
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {}
|
|||||||
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
|
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
|
||||||
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
|
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
|
||||||
|
|
||||||
|
// Attach and Detach are no-ops: the USB Serial/JTAG controller has no
|
||||||
|
// software-controlled soft-connect, it is always attached to the bus.
|
||||||
|
func (dev *USBDevice) Attach() {}
|
||||||
|
func (dev *USBDevice) Detach() {}
|
||||||
|
|
||||||
// initUSB is intentionally empty — the interp phase evaluates init()
|
// initUSB is intentionally empty — the interp phase evaluates init()
|
||||||
// functions at compile time and cannot access hardware registers.
|
// functions at compile time and cannot access hardware registers.
|
||||||
// Actual hardware setup is deferred to the first Configure() call.
|
// Actual hardware setup is deferred to the first Configure() call.
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {}
|
|||||||
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
|
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
|
||||||
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
|
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
|
||||||
|
|
||||||
|
// Attach and Detach are no-ops: the USB Serial/JTAG controller has no
|
||||||
|
// software-controlled soft-connect, it is always attached to the bus.
|
||||||
|
func (dev *USBDevice) Attach() {}
|
||||||
|
func (dev *USBDevice) Detach() {}
|
||||||
|
|
||||||
// initUSB is intentionally empty — the interp phase evaluates init()
|
// initUSB is intentionally empty — the interp phase evaluates init()
|
||||||
// functions at compile time and cannot access hardware registers.
|
// functions at compile time and cannot access hardware registers.
|
||||||
// Actual hardware setup is deferred to the first Configure() call.
|
// Actual hardware setup is deferred to the first Configure() call.
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {}
|
|||||||
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
|
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
|
||||||
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
|
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
|
||||||
|
|
||||||
|
// Attach and Detach are no-ops: the USB Serial/JTAG controller has no
|
||||||
|
// software-controlled soft-connect, it is always attached to the bus.
|
||||||
|
func (dev *USBDevice) Attach() {}
|
||||||
|
func (dev *USBDevice) Detach() {}
|
||||||
|
|
||||||
// initUSB is intentionally empty — the interp phase evaluates init()
|
// initUSB is intentionally empty — the interp phase evaluates init()
|
||||||
// functions at compile time and cannot access hardware registers.
|
// functions at compile time and cannot access hardware registers.
|
||||||
// Actual hardware setup is deferred to the first Configure() call.
|
// Actual hardware setup is deferred to the first Configure() call.
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ var (
|
|||||||
epinen uint32
|
epinen uint32
|
||||||
epouten uint32
|
epouten uint32
|
||||||
easyDMABusy volatile.Register8
|
easyDMABusy volatile.Register8
|
||||||
|
|
||||||
|
// usbDetached keeps the device detached from the bus after Detach: the
|
||||||
|
// USB IRQ handler re-enables the DP pull-up on every power-ready event,
|
||||||
|
// which would otherwise silently undo a Detach.
|
||||||
|
usbDetached bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// enterCriticalSection is used to protect access to easyDMA - only one thing
|
// enterCriticalSection is used to protect access to easyDMA - only one thing
|
||||||
@@ -89,6 +94,23 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
dev.initcomplete = true
|
dev.initcomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach connects the device to the USB bus by enabling the DP pull-up,
|
||||||
|
// allowing the host to detect and enumerate it. It can be used together with
|
||||||
|
// Detach to delay enumeration until the USB configuration (device
|
||||||
|
// identifiers, classes, ...) is complete.
|
||||||
|
func (dev *USBDevice) Attach() {
|
||||||
|
usbDetached = false
|
||||||
|
nrf.USBD.USBPULLUP.Set(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
|
||||||
|
// To the host this appears as if the device was unplugged. A subsequent
|
||||||
|
// Attach makes the host enumerate the device again.
|
||||||
|
func (dev *USBDevice) Detach() {
|
||||||
|
usbDetached = true
|
||||||
|
nrf.USBD.USBPULLUP.Set(0)
|
||||||
|
}
|
||||||
|
|
||||||
func handleUSBIRQ(interrupt.Interrupt) {
|
func handleUSBIRQ(interrupt.Interrupt) {
|
||||||
if nrf.USBD.EVENTS_SOF.Get() == 1 {
|
if nrf.USBD.EVENTS_SOF.Get() == 1 {
|
||||||
nrf.USBD.EVENTS_SOF.Set(0)
|
nrf.USBD.EVENTS_SOF.Set(0)
|
||||||
@@ -103,7 +125,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
|
|||||||
|
|
||||||
// Configure control endpoint
|
// Configure control endpoint
|
||||||
initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL)
|
initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL)
|
||||||
|
if !usbDetached {
|
||||||
nrf.USBD.USBPULLUP.Set(1)
|
nrf.USBD.USBPULLUP.Set(1)
|
||||||
|
}
|
||||||
|
|
||||||
usbConfiguration = 0
|
usbConfiguration = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,24 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
rp.USBCTRL_REGS_INTE_SETUP_REQ)
|
rp.USBCTRL_REGS_INTE_SETUP_REQ)
|
||||||
|
|
||||||
// Present full speed device by enabling pull up on DP
|
// Present full speed device by enabling pull up on DP
|
||||||
|
dev.Attach()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach connects the device to the USB bus by enabling the DP pull-up,
|
||||||
|
// allowing the host to detect and enumerate it. It can be used together with
|
||||||
|
// Detach to delay enumeration until the USB configuration (device
|
||||||
|
// identifiers, classes, ...) is complete.
|
||||||
|
func (dev *USBDevice) Attach() {
|
||||||
rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
|
rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
|
||||||
|
// To the host this appears as if the device was unplugged. A subsequent
|
||||||
|
// Attach makes the host enumerate the device again.
|
||||||
|
func (dev *USBDevice) Detach() {
|
||||||
|
rp.USBCTRL_REGS.SIE_CTRL.ClearBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
|
||||||
|
}
|
||||||
|
|
||||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||||
status := rp.USBCTRL_REGS.INTS.Get()
|
status := rp.USBCTRL_REGS.INTS.Get()
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,27 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
rp.USB_INTE_SETUP_REQ)
|
rp.USB_INTE_SETUP_REQ)
|
||||||
|
|
||||||
// Present full speed device by enabling pull up on DP
|
// Present full speed device by enabling pull up on DP
|
||||||
rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN)
|
dev.Attach()
|
||||||
|
|
||||||
// 12.7.2 Disable phy isolation
|
// 12.7.2 Disable phy isolation
|
||||||
rp.USB.SetMAIN_CTRL_PHY_ISO(0x0)
|
rp.USB.SetMAIN_CTRL_PHY_ISO(0x0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach connects the device to the USB bus by enabling the DP pull-up,
|
||||||
|
// allowing the host to detect and enumerate it. It can be used together with
|
||||||
|
// Detach to delay enumeration until the USB configuration (device
|
||||||
|
// identifiers, classes, ...) is complete.
|
||||||
|
func (dev *USBDevice) Attach() {
|
||||||
|
rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
|
||||||
|
// To the host this appears as if the device was unplugged. A subsequent
|
||||||
|
// Attach makes the host enumerate the device again.
|
||||||
|
func (dev *USBDevice) Detach() {
|
||||||
|
rp.USB.SIE_CTRL.ClearBits(rp.USB_SIE_CTRL_PULLUP_EN)
|
||||||
|
}
|
||||||
|
|
||||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||||
status := rp.USB.INTS.Get()
|
status := rp.USB.INTS.Get()
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
otgPower.PCGCCTL.Set(0)
|
otgPower.PCGCCTL.Set(0)
|
||||||
|
|
||||||
// Soft-disconnect now (after CSRST reset DCTL to its default connected state).
|
// Soft-disconnect now (after CSRST reset DCTL to its default connected state).
|
||||||
otgDevice.DCTL.SetBits(dctlSDIS)
|
dev.Detach()
|
||||||
|
|
||||||
// ---- 7. Configure data FIFOs --------------------------------------------
|
// ---- 7. Configure data FIFOs --------------------------------------------
|
||||||
|
|
||||||
@@ -311,11 +311,26 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
|
|
||||||
// ---- 12. Connect to host (clear soft-disconnect) -----------------------
|
// ---- 12. Connect to host (clear soft-disconnect) -----------------------
|
||||||
|
|
||||||
otgDevice.DCTL.ClearBits(dctlSDIS)
|
dev.Attach()
|
||||||
|
|
||||||
dev.initcomplete = true
|
dev.initcomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach connects the device to the USB bus by releasing soft disconnect,
|
||||||
|
// allowing the host to detect and enumerate it. It can be used together with
|
||||||
|
// Detach to delay enumeration until the USB configuration (device
|
||||||
|
// identifiers, classes, ...) is complete.
|
||||||
|
func (dev *USBDevice) Attach() {
|
||||||
|
otgDevice.DCTL.ClearBits(dctlSDIS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach disconnects the device from the USB bus by asserting soft
|
||||||
|
// disconnect. To the host this appears as if the device was unplugged. A
|
||||||
|
// subsequent Attach makes the host enumerate the device again.
|
||||||
|
func (dev *USBDevice) Detach() {
|
||||||
|
otgDevice.DCTL.SetBits(dctlSDIS)
|
||||||
|
}
|
||||||
|
|
||||||
// handleUSBIRQ is the OTG FS interrupt handler, dispatching on GINTSTS bits.
|
// handleUSBIRQ is the OTG FS interrupt handler, dispatching on GINTSTS bits.
|
||||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||||
status := stm32.OTG_FS_GLOBAL.GINTSTS.Get() &
|
status := stm32.OTG_FS_GLOBAL.GINTSTS.Get() &
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
|
|
||||||
// Stay soft-disconnected until configuration is complete; CSRST left
|
// Stay soft-disconnected until configuration is complete; CSRST left
|
||||||
// DCTL at its default "connected" state.
|
// DCTL at its default "connected" state.
|
||||||
usbOTG.DCTL.SetBits(DCTL_SDIS)
|
dev.Detach()
|
||||||
|
|
||||||
// 5. Force device mode now that the core is out of reset. The mode
|
// 5. Force device mode now that the core is out of reset. The mode
|
||||||
// change takes effect only after up to 25 ms (RM0433); poll GINTSTS.CMOD
|
// change takes effect only after up to 25 ms (RM0433); poll GINTSTS.CMOD
|
||||||
@@ -265,9 +265,24 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
|||||||
dev.initcomplete = true
|
dev.initcomplete = true
|
||||||
|
|
||||||
// Release soft-disconnect: pulls D+ high, making device visible to host.
|
// Release soft-disconnect: pulls D+ high, making device visible to host.
|
||||||
|
dev.Attach()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach connects the device to the USB bus by releasing soft disconnect,
|
||||||
|
// allowing the host to detect and enumerate it. It can be used together with
|
||||||
|
// Detach to delay enumeration until the USB configuration (device
|
||||||
|
// identifiers, classes, ...) is complete.
|
||||||
|
func (dev *USBDevice) Attach() {
|
||||||
usbOTG.DCTL.ClearBits(DCTL_SDIS)
|
usbOTG.DCTL.ClearBits(DCTL_SDIS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detach disconnects the device from the USB bus by asserting soft
|
||||||
|
// disconnect. To the host this appears as if the device was unplugged. A
|
||||||
|
// subsequent Attach makes the host enumerate the device again.
|
||||||
|
func (dev *USBDevice) Detach() {
|
||||||
|
usbOTG.DCTL.SetBits(DCTL_SDIS)
|
||||||
|
}
|
||||||
|
|
||||||
func initEndpoint(ep, config uint32) {
|
func initEndpoint(ep, config uint32) {
|
||||||
if ep == 0 {
|
if ep == 0 {
|
||||||
// Control endpoint
|
// Control endpoint
|
||||||
|
|||||||
@@ -163,6 +163,17 @@ func (f *File) Truncate(size int64) (err error) {
|
|||||||
return Truncate(f.name, size)
|
return Truncate(f.name, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chown changes the numeric uid and gid of the named file.
|
||||||
|
// A uid or gid of -1 means to not change that value.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func (f *File) Chown(uid, gid int) error {
|
||||||
|
if f.handle == nil {
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
return Chown(f.name, uid, gid)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *File) chmod(mode FileMode) error {
|
func (f *File) chmod(mode FileMode) error {
|
||||||
if f.handle == nil {
|
if f.handle == nil {
|
||||||
return ErrClosed
|
return ErrClosed
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package os
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// Getpagesize returns the underlying system's memory page size.
|
||||||
|
func Getpagesize() int { return syscall.Getpagesize() }
|
||||||
@@ -6,11 +6,6 @@
|
|||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
// Getpagesize returns the underlying system's memory page size.
|
|
||||||
func Getpagesize() int { return syscall.Getpagesize() }
|
|
||||||
|
|
||||||
func (fs *fileStat) Name() string { return fs.name }
|
func (fs *fileStat) Name() string { return fs.name }
|
||||||
func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() }
|
func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() }
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ var (
|
|||||||
endBlock gcBlock // the block just past the end of the available space
|
endBlock gcBlock // the block just past the end of the available space
|
||||||
gcTotalAlloc uint64 // total number of bytes allocated
|
gcTotalAlloc uint64 // total number of bytes allocated
|
||||||
gcMallocs uint64 // total number of allocations
|
gcMallocs uint64 // total number of allocations
|
||||||
|
gcNumGC uint32 // total number of completed collection cycles
|
||||||
gcLock task.PMutex // lock to avoid race conditions on multicore systems
|
gcLock task.PMutex // lock to avoid race conditions on multicore systems
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -510,7 +511,7 @@ func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
|||||||
lastBlock := firstBlock.findHead()
|
lastBlock := firstBlock.findHead()
|
||||||
|
|
||||||
// Calculate the size of the original allocation body.
|
// Calculate the size of the original allocation body.
|
||||||
oldSize := uintptr(lastBlock-firstBlock)*blocksPerStateByte + (bytesPerBlock - unsafe.Sizeof(objHeader{}))
|
oldSize := uintptr(lastBlock-firstBlock)*bytesPerBlock + (bytesPerBlock - unsafe.Sizeof(objHeader{}))
|
||||||
|
|
||||||
if size <= oldSize {
|
if size <= oldSize {
|
||||||
// The requested size is less than the old size.
|
// The requested size is less than the old size.
|
||||||
@@ -610,6 +611,11 @@ func runGC() (freeBytes uintptr) {
|
|||||||
dumpHeap()
|
dumpHeap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The cycle is complete. Counted here rather than in GC() so that
|
||||||
|
// collections triggered by an allocation are counted too. Every caller
|
||||||
|
// holds gcLock, the same lock ReadMemStats reads it under.
|
||||||
|
gcNumGC++
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,6 +856,9 @@ func ReadMemStats(m *MemStats) {
|
|||||||
// Record the total allocated bytes.
|
// Record the total allocated bytes.
|
||||||
m.TotalAlloc = gcTotalAlloc
|
m.TotalAlloc = gcTotalAlloc
|
||||||
|
|
||||||
|
// Record the number of completed collection cycles.
|
||||||
|
m.NumGC = gcNumGC
|
||||||
|
|
||||||
gcLock.Unlock()
|
gcLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ func ReadMemStats(m *MemStats) {
|
|||||||
m.Mallocs = 0 // not provided by bdwgc
|
m.Mallocs = 0 // not provided by bdwgc
|
||||||
m.Frees = 0 // not provided by bdwgc
|
m.Frees = 0 // not provided by bdwgc
|
||||||
m.Sys = uint64(gcMemStats.obtained_from_os_bytes)
|
m.Sys = uint64(gcMemStats.obtained_from_os_bytes)
|
||||||
|
m.NumGC = uint32(gcMemStats.gc_no)
|
||||||
|
|
||||||
gcLock.Unlock()
|
gcLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,14 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
|||||||
gcTotalAlloc += uint64(size)
|
gcTotalAlloc += uint64(size)
|
||||||
gcMallocs++
|
gcMallocs++
|
||||||
heapptr += size
|
heapptr += size
|
||||||
for heapptr >= heapEnd {
|
if heapptr < addr {
|
||||||
|
// The allocation size overflowed the heap pointer.
|
||||||
|
runtimePanic("out of memory")
|
||||||
|
}
|
||||||
|
for heapptr > heapEnd {
|
||||||
// Try to increase the heap and check again.
|
// Try to increase the heap and check again.
|
||||||
|
// Use > instead of >= because an allocation that exactly
|
||||||
|
// ends at heapEnd is still within heap boundaries.
|
||||||
if growHeap() {
|
if growHeap() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -103,6 +109,7 @@ func ReadMemStats(m *MemStats) {
|
|||||||
m.HeapAlloc = gcTotalAlloc
|
m.HeapAlloc = gcTotalAlloc
|
||||||
m.HeapObjects = gcMallocs
|
m.HeapObjects = gcMallocs
|
||||||
m.Alloc = m.HeapAlloc
|
m.Alloc = m.HeapAlloc
|
||||||
|
m.NumGC = 0 // this GC never collects, so no cycle ever completes
|
||||||
|
|
||||||
gcLock.Unlock()
|
gcLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ func gcMarkReachable() {
|
|||||||
gcPauseCore(i)
|
gcPauseCore(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Busy-wait until all the other cores are ready.
|
||||||
|
for gcScanState.Load() != numCPU {
|
||||||
|
spinLoopWait()
|
||||||
|
}
|
||||||
|
gcScanState.Store(0)
|
||||||
|
|
||||||
// Scan the stack(s) of the current core.
|
// Scan the stack(s) of the current core.
|
||||||
scanCurrentStack()
|
scanCurrentStack()
|
||||||
if !task.OnSystemStack() {
|
if !task.OnSystemStack() {
|
||||||
@@ -53,13 +59,6 @@ func gcMarkReachable() {
|
|||||||
// Scan globals.
|
// Scan globals.
|
||||||
findGlobals(markRoots)
|
findGlobals(markRoots)
|
||||||
|
|
||||||
// Busy-wait until all the other cores are ready. They certainly should be,
|
|
||||||
// after the scanning we did above.
|
|
||||||
for gcScanState.Load() != numCPU {
|
|
||||||
spinLoopWait()
|
|
||||||
}
|
|
||||||
gcScanState.Store(0)
|
|
||||||
|
|
||||||
// Signal each core in turn that they can scan the stack.
|
// Signal each core in turn that they can scan the stack.
|
||||||
for i := uint32(0); i < numCPU; i++ {
|
for i := uint32(0); i < numCPU; i++ {
|
||||||
if i == core {
|
if i == core {
|
||||||
|
|||||||
@@ -807,13 +807,13 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
|
|||||||
case reflectlite.Array:
|
case reflectlite.Array:
|
||||||
var hash uint32
|
var hash uint32
|
||||||
for i := 0; i < x.Len(); i++ {
|
for i := 0; i < x.Len(); i++ {
|
||||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
|
hash = (hash * 31) ^ hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
|
||||||
}
|
}
|
||||||
return hash
|
return hash
|
||||||
case reflectlite.Struct:
|
case reflectlite.Struct:
|
||||||
var hash uint32
|
var hash uint32
|
||||||
for i := 0; i < x.NumField(); i++ {
|
for i := 0; i < x.NumField(); i++ {
|
||||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
|
hash = (hash * 31) ^ hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
|
||||||
}
|
}
|
||||||
return hash
|
return hash
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -82,4 +82,9 @@ type MemStats struct {
|
|||||||
|
|
||||||
// GCSys is bytes of memory in garbage collection metadata.
|
// GCSys is bytes of memory in garbage collection metadata.
|
||||||
GCSys uint64
|
GCSys uint64
|
||||||
|
|
||||||
|
// NumGC is the number of completed GC cycles.
|
||||||
|
//
|
||||||
|
// The leaking collector never collects, so it always reports 0.
|
||||||
|
NumGC uint32
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,12 +78,6 @@ var _ebss [0]byte
|
|||||||
//go:extern _vector_table
|
//go:extern _vector_table
|
||||||
var _vector_table [0]uintptr
|
var _vector_table [0]uintptr
|
||||||
|
|
||||||
func abort() {
|
|
||||||
for {
|
|
||||||
device.Asm("waiti 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// interruptInit installs the Xtensa vector table by writing its address
|
// interruptInit installs the Xtensa vector table by writing its address
|
||||||
// to the VECBASE special register and ensures all CPU interrupts are
|
// to the VECBASE special register and ensures all CPU interrupts are
|
||||||
// initially disabled.
|
// initially disabled.
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build esp32 && !qemu
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "device"
|
||||||
|
|
||||||
|
func abort() {
|
||||||
|
for {
|
||||||
|
device.Asm("waiti 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//go:build esp32 && qemu
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "device"
|
||||||
|
|
||||||
|
func exit(code int) {
|
||||||
|
qemuExit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func abort() {
|
||||||
|
qemuExit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func qemuExit(code int) {
|
||||||
|
// QEMU semihosting expects a2 = SYS_exit (1) and a3 = the exit code.
|
||||||
|
// AsmFull cannot declare these clobbers, so no Go code may run afterward.
|
||||||
|
device.AsmFull(
|
||||||
|
"mov a3, {code}\n"+
|
||||||
|
"movi a2, 1\n"+
|
||||||
|
"simcall",
|
||||||
|
map[string]interface{}{"code": code},
|
||||||
|
)
|
||||||
|
// This loop ensures the clobbered registers are never used again.
|
||||||
|
for {
|
||||||
|
device.Asm("waiti 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,10 +55,6 @@ func ticksToNanoseconds(ticks timeUnit) int64 {
|
|||||||
return int64(ticks) * 25
|
return int64(ticks) * 25
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit(code int) {
|
|
||||||
abort()
|
|
||||||
}
|
|
||||||
|
|
||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
machine.Serial.WriteByte(c)
|
machine.Serial.WriteByte(c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build (esp32 && !qemu) || esp32c3 || esp32c6
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
func exit(code int) {
|
||||||
|
abort()
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
//go:build baremetal || js || wasip1 || wasip2 || wasm_unknown || nintendoswitch
|
||||||
|
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package syscall
|
||||||
|
|
||||||
|
// Statfs_t and Fsid have been copied from the Go source tree
|
||||||
|
// (syscall/ztypes_linux_amd64.go).
|
||||||
|
|
||||||
|
type Statfs_t struct {
|
||||||
|
Type int64
|
||||||
|
Bsize int64
|
||||||
|
Blocks uint64
|
||||||
|
Bfree uint64
|
||||||
|
Bavail uint64
|
||||||
|
Files uint64
|
||||||
|
Ffree uint64
|
||||||
|
Fsid Fsid
|
||||||
|
Namelen int64
|
||||||
|
Frsize int64
|
||||||
|
Flags int64
|
||||||
|
Spare [4]int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fsid struct {
|
||||||
|
X__val [2]int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a stub, it is not functional.
|
||||||
|
func Statfs(path string, buf *Statfs_t) (err error) {
|
||||||
|
return ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a stub, it is not functional.
|
||||||
|
func Fstatfs(fd int, buf *Statfs_t) (err error) {
|
||||||
|
return ENOSYS
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"inherits": ["esp32-generic"],
|
||||||
|
"build-tags": ["qemu"],
|
||||||
|
"emulator": "qemu-system-xtensa -machine esp32 -nographic -semihosting -drive file={img},if=mtd,format=raw"
|
||||||
|
}
|
||||||
@@ -25,6 +25,5 @@
|
|||||||
],
|
],
|
||||||
"binary-format": "esp32",
|
"binary-format": "esp32",
|
||||||
"flash-method": "esp32flash",
|
"flash-method": "esp32flash",
|
||||||
"emulator": "qemu-system-xtensa -machine esp32 -nographic -drive file={img},if=mtd,format=raw",
|
|
||||||
"gdb": ["xtensa-esp32-elf-gdb"]
|
"gdb": ["xtensa-esp32-elf-gdb"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,13 +299,37 @@ _handle_kernel_exc:
|
|||||||
|
|
||||||
.global _handle_level1
|
.global _handle_level1
|
||||||
_handle_level1:
|
_handle_level1:
|
||||||
// --- allocate 96-byte exception frame on the interrupted stack ---
|
// --- EXCCAUSE 5 (alloca / MOVSP) is not a fault, it is a request ---
|
||||||
|
//
|
||||||
|
// MOVSP raises this whenever it moves the stack pointer while the caller's
|
||||||
|
// register window is still live in the register file: the hardware is
|
||||||
|
// asking us to spill that window, not reporting an error. LLVM emits MOVSP
|
||||||
|
// for any call that needs a 7th argument on the stack (the windowed ABI
|
||||||
|
// passes six in registers), so it shows up in ordinary C and Go code alike.
|
||||||
|
//
|
||||||
|
// Without this, such a call takes a fatal user exception. The check goes
|
||||||
|
// first, before any state is touched, so a0 is still in EXCSAVE1 where
|
||||||
|
// _xt_alloca_exc wants it and every other cause falls through to the
|
||||||
|
// handler below completely unchanged.
|
||||||
|
rsr a0, EXCCAUSE
|
||||||
|
bnei a0, 5, 1f
|
||||||
|
j _xt_alloca_exc
|
||||||
|
1:
|
||||||
|
rsr a0, EXCSAVE1 // restore a0 clobbered by the EXCCAUSE read
|
||||||
|
|
||||||
|
// --- allocate the exception frame on the interrupted stack ---
|
||||||
// Layout (offsets from a1 after adjustment):
|
// Layout (offsets from a1 after adjustment):
|
||||||
// 0: a0 4: a1(orig) 8: a2 12: a3 16: a4 20: a5
|
// 0: a0 4: a1(orig) 8: a2 12: a3 16: a4 20: a5
|
||||||
// 24: a6 28: a7 32: a8 36: a9 40: a10 44: a11
|
// 24: a6 28: a7 32: a8 36: a9 40: a10 44: a11
|
||||||
// 48: a12 52: a13 56: a14 60: a15
|
// 48: a12 52: a13 56: a14 60: a15
|
||||||
// 64: SAR 68: EPC1 72: PS
|
// 64: SAR 68: EPC1 72: PS 76: WINDOWBASE 80: WINDOWSTART
|
||||||
addi a0, a1, -96 // a0 = new frame pointer
|
//
|
||||||
|
// 128, not the 84 bytes the layout above needs: the 16 bytes below the
|
||||||
|
// interrupted SP are the windowed-ABI caller save area, written by window
|
||||||
|
// overflow and read back by underflow. Clobbering them corrupts the
|
||||||
|
// interrupted code's a0, which surfaces later as an Illegal Instruction on
|
||||||
|
// its retw. ESP-IDF reserves the same gap (the 0x20 in XT_STK_FRMSZ).
|
||||||
|
addi a0, a1, -128 // a0 = new frame pointer
|
||||||
s32i a1, a0, 4 // save original a1 (SP)
|
s32i a1, a0, 4 // save original a1 (SP)
|
||||||
mov a1, a0 // a1 = frame pointer
|
mov a1, a0 // a1 = frame pointer
|
||||||
|
|
||||||
@@ -339,6 +363,15 @@ _handle_level1:
|
|||||||
// level-1 interrupts.
|
// level-1 interrupts.
|
||||||
rsr a2, PS
|
rsr a2, PS
|
||||||
s32i a2, a1, 72 // save PS (with EXCM=1 set by hardware)
|
s32i a2, a1, 72 // save PS (with EXCM=1 set by hardware)
|
||||||
|
|
||||||
|
// Window state as it was at the fault. Reading it from the C handler
|
||||||
|
// instead describes the handler: getting there costs a callx4, and the
|
||||||
|
// printfs rotate and spill windows of their own.
|
||||||
|
rsr a3, WINDOWBASE
|
||||||
|
s32i a3, a1, 76
|
||||||
|
rsr a3, WINDOWSTART
|
||||||
|
s32i a3, a1, 80
|
||||||
|
|
||||||
movi a3, ~0x1F // mask: clear INTLEVEL (bits 0-3) + EXCM (bit 4)
|
movi a3, ~0x1F // mask: clear INTLEVEL (bits 0-3) + EXCM (bit 4)
|
||||||
and a2, a2, a3
|
and a2, a2, a3
|
||||||
movi a3, 1 // INTLEVEL = 1
|
movi a3, 1 // INTLEVEL = 1
|
||||||
@@ -412,6 +445,52 @@ _handle_level1:
|
|||||||
|
|
||||||
rfe
|
rfe
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Alloca (MOVSP) exception handler.
|
||||||
|
//
|
||||||
|
// Rotates back to the window that executed MOVSP, fixes up PS.OWB to match
|
||||||
|
// the new WINDOWBASE, and falls into the window underflow handler that
|
||||||
|
// corresponds to the caller's call size. The underflow handler spills the
|
||||||
|
// window and returns to the interrupted MOVSP via rfwu, which then completes.
|
||||||
|
//
|
||||||
|
// All of the interruptee's registers are intact except a0, which is in
|
||||||
|
// EXCSAVE1. PS.EXCM is set by the hardware, so this cannot be interrupted.
|
||||||
|
// The registers of the base-save area are free scratch here, because taking
|
||||||
|
// this exception means they have already been spilled and the underflow
|
||||||
|
// handler will restore them.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
.balign 4
|
||||||
|
.global _xt_alloca_exc
|
||||||
|
_xt_alloca_exc:
|
||||||
|
rsr a0, WINDOWBASE // grab WINDOWBASE before rotw changes it
|
||||||
|
rotw -1 // WINDOWBASE goes to a4, new a0-a3 are scratch
|
||||||
|
rsr a2, PS
|
||||||
|
extui a3, a2, 8, 4 // a3 = PS.OWB (shift 8, 4 bits)
|
||||||
|
xor a3, a3, a4 // bits that changed from old to current WB
|
||||||
|
rsr a4, EXCSAVE1 // restore the interruptee's a0 (now in a4)
|
||||||
|
slli a3, a3, 8
|
||||||
|
xor a2, a2, a3 // flip those bits in PS.OWB
|
||||||
|
wsr a2, PS // PS.OWB now matches the new WINDOWBASE
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Dispatch on the call size encoded in the interruptee's return address:
|
||||||
|
// bits 31:30 of a0 are 00/01 for call4, 10 for call8, 11 for call12.
|
||||||
|
//
|
||||||
|
// The canonical sequence branches straight to the underflow vectors with
|
||||||
|
// _bbci.l, but that has only an 8-bit displacement and this handler lives
|
||||||
|
// outside the vector table, well out of reach. Inverting each test and
|
||||||
|
// putting the vector target on a full-range `j` is the same dispatch
|
||||||
|
// without the range limit.
|
||||||
|
_bbsi.l a4, 31, 1f
|
||||||
|
j _window_underflow4
|
||||||
|
1:
|
||||||
|
rotw -1 // interruptee's a0 moves to a8
|
||||||
|
_bbsi.l a8, 30, 2f
|
||||||
|
j _window_underflow8
|
||||||
|
2:
|
||||||
|
rotw -1
|
||||||
|
j _window_underflow12
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Default weak espradio_user_exception: infinite loop halt.
|
// Default weak espradio_user_exception: infinite loop halt.
|
||||||
// Overridden by the strong definition in espradio's isr.c when linked.
|
// Overridden by the strong definition in espradio's isr.c when linked.
|
||||||
|
|||||||
@@ -25,6 +25,5 @@
|
|||||||
],
|
],
|
||||||
"binary-format": "esp32s3",
|
"binary-format": "esp32s3",
|
||||||
"flash-method": "esp32jtag",
|
"flash-method": "esp32jtag",
|
||||||
"emulator": "qemu-system-xtensa -machine esp32 -nographic -drive file={img},if=mtd,format=raw",
|
|
||||||
"gdb": ["xtensa-esp32-elf-gdb"]
|
"gdb": ["xtensa-esp32-elf-gdb"]
|
||||||
}
|
}
|
||||||
|
|||||||
+1017
-2
File diff suppressed because it is too large
Load Diff
+29
-24
@@ -76,7 +76,7 @@ func testTransform(t *testing.T, pathPrefix string, transform func(mod llvm.Modu
|
|||||||
actual = actual[strings.Index(actual, "\ntarget datalayout = ")+1:]
|
actual = actual[strings.Index(actual, "\ntarget datalayout = ")+1:]
|
||||||
|
|
||||||
if *update {
|
if *update {
|
||||||
err := os.WriteFile(pathPrefix+".out.ll", []byte(actual), 0666)
|
err := os.WriteFile(pathPrefix+".out.ll", []byte(normalizeIR(actual)), 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to write out new output:", err)
|
t.Error("failed to write out new output:", err)
|
||||||
}
|
}
|
||||||
@@ -100,28 +100,8 @@ func testTransform(t *testing.T, pathPrefix string, transform func(mod llvm.Modu
|
|||||||
// equal. That means, only relevant lines are compared (excluding comments
|
// equal. That means, only relevant lines are compared (excluding comments
|
||||||
// etc.).
|
// etc.).
|
||||||
func fuzzyEqualIR(s1, s2 string) bool {
|
func fuzzyEqualIR(s1, s2 string) bool {
|
||||||
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
s1 = normalizeIR(s1)
|
||||||
// which LLVM printed before any co-occurring attribute such as
|
s2 = normalizeIR(s2)
|
||||||
// 'readonly' (e.g. "ptr nocapture readonly"). LLVM 21+ prints the
|
|
||||||
// equivalent 'captures(none)' instead, and after such attributes (e.g.
|
|
||||||
// "ptr readonly captures(none)"). Normalize both name and position back
|
|
||||||
// to the old spelling to keep a single golden file working across LLVM
|
|
||||||
// versions.
|
|
||||||
s1 = normalizeCapturesAttr(s1)
|
|
||||||
s2 = normalizeCapturesAttr(s2)
|
|
||||||
|
|
||||||
// LLVM 21+ also added an explicit 'nocreateundeforpoison' attribute to
|
|
||||||
// certain intrinsic declarations (e.g. llvm.umin) that were implicitly
|
|
||||||
// assumed not to create undef/poison before. It's unrelated to the
|
|
||||||
// escape-analysis behavior under test, so ignore it for comparison.
|
|
||||||
s1 = strings.ReplaceAll(s1, "nocreateundeforpoison ", "")
|
|
||||||
s2 = strings.ReplaceAll(s2, "nocreateundeforpoison ", "")
|
|
||||||
|
|
||||||
// LLVM 22 dropped the (redundant) i64 size argument from
|
|
||||||
// llvm.lifetime.start/end. Normalize away that argument so golden files
|
|
||||||
// written against the two-argument form still match.
|
|
||||||
s1 = lifetimeSizeArgRe.ReplaceAllString(s1, "$1")
|
|
||||||
s2 = lifetimeSizeArgRe.ReplaceAllString(s2, "$1")
|
|
||||||
|
|
||||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
||||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
||||||
@@ -138,6 +118,29 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeIR(s string) string {
|
||||||
|
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
||||||
|
// which LLVM printed before any co-occurring attribute such as
|
||||||
|
// 'readonly' (e.g. "ptr nocapture readonly"). LLVM 21+ prints the
|
||||||
|
// equivalent 'captures(none)' instead, and after such attributes (e.g.
|
||||||
|
// "ptr readonly captures(none)"). Normalize both name and position back
|
||||||
|
// to the old spelling.
|
||||||
|
s = normalizeCapturesAttr(s)
|
||||||
|
|
||||||
|
// LLVM 21+ also added an explicit 'nocreateundeforpoison' attribute to
|
||||||
|
// certain intrinsic declarations (e.g. llvm.umin) that were implicitly
|
||||||
|
// assumed not to create undef/poison before. It's unrelated to the
|
||||||
|
// escape-analysis behavior under test, so ignore it for comparison.
|
||||||
|
s = strings.ReplaceAll(s, "nocreateundeforpoison ", "")
|
||||||
|
|
||||||
|
// LLVM 22 dropped the (redundant) i64 size argument from
|
||||||
|
// llvm.lifetime.start/end. Normalize away that argument so golden files
|
||||||
|
// written against the two-argument form still match.
|
||||||
|
s = lifetimeSizeArgRe.ReplaceAllString(s, "$1")
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// capturesNoneAttrRe matches a co-occurring attribute directly followed by
|
// capturesNoneAttrRe matches a co-occurring attribute directly followed by
|
||||||
// 'captures(none)', which is how LLVM 21+ orders these two attributes when
|
// 'captures(none)', which is how LLVM 21+ orders these two attributes when
|
||||||
// printing IR (the pre-LLVM21 'nocapture' attribute printed the other way
|
// printing IR (the pre-LLVM21 'nocapture' attribute printed the other way
|
||||||
@@ -221,7 +224,9 @@ func compileGoFileForTesting(t *testing.T, filename string) llvm.Module {
|
|||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
pkg := lprogram.MainPkg()
|
pkg := lprogram.MainPkg()
|
||||||
mod, errs := compiler.CompilePackage(filename, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
ssaPkg := program.Package(pkg.Pkg)
|
||||||
|
ssaPkg.Build()
|
||||||
|
mod, errs := compiler.CompilePackage(filename, pkg, ssaPkg, machine, compilerConfig, false)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|||||||
Reference in New Issue
Block a user