mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-21 12:59: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
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Run go mod tidy
|
||||
run: go mod tidy
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.27rc2-alpine
|
||||
image: golang:1.27-alpine
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
@@ -196,6 +196,10 @@ jobs:
|
||||
cat /proc/cpuinfo
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
libgcrypt20 \
|
||||
libpixman-1-0 \
|
||||
libsdl2-2.0-0 \
|
||||
libslirp0 \
|
||||
qemu-system-arm \
|
||||
qemu-system-riscv32 \
|
||||
qemu-user \
|
||||
@@ -204,7 +208,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v6
|
||||
@@ -216,6 +220,14 @@ jobs:
|
||||
version: "29.0.1"
|
||||
- name: Setup `wasm-tools`
|
||||
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
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
@@ -323,7 +335,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v5
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
go-version: '1.27.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v8
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.27rc2 AS tinygo-llvm
|
||||
FROM golang:1.27 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
||||
@@ -33,7 +33,7 @@ RUN cd /tinygo/ && \
|
||||
|
||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||
# all the build tools etc).
|
||||
FROM golang:1.27rc2 AS tinygo-compiler
|
||||
FROM golang:1.27 AS tinygo-compiler
|
||||
|
||||
# Copy tinygo build.
|
||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||
|
||||
+23
-2
@@ -473,9 +473,15 @@ TEST_PACKAGES_NONWASM = \
|
||||
# (just like wasm).
|
||||
# * picolibc math functions apparently are less precise, the math package
|
||||
# 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_NONBAREMETAL = \
|
||||
$(TEST_PACKAGES_NONWASM) \
|
||||
$(TEST_PACKAGES_NOBOUNDARYSLICES) \
|
||||
math \
|
||||
$(nil)
|
||||
|
||||
@@ -484,6 +490,19 @@ TEST_PACKAGES_NOWASI = \
|
||||
crypto/ecdsa \
|
||||
$(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-stdlib-tests-pass:
|
||||
$(eval jointmp := $(shell echo /tmp/join.$$$$))
|
||||
@@ -510,7 +529,7 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation'
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation|TestUnmarshalNestingLimitSlice|TestUnmarshalNestingLimitStruct'
|
||||
TEST_ADDITIONAL_FLAGS ?=
|
||||
|
||||
# Test known-working standard library packages.
|
||||
@@ -519,6 +538,8 @@ TEST_ADDITIONAL_FLAGS ?=
|
||||
tinygo-test:
|
||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||
@# TestParseAndBytesRoundTrip/P256/Generic: needs Goexit to run defers on wasm.
|
||||
@# 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)
|
||||
ifeq ($(TEST_ENCODING_XML),true)
|
||||
$(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 -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-wasip2-fast:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASI) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASIP2) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-sum-slow:
|
||||
TINYGO=$(TINYGO) \
|
||||
|
||||
+9
-2
@@ -24,6 +24,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gofrs/flock"
|
||||
"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
|
||||
// program so it's pretty fast and doesn't need to be parallelized.
|
||||
program := lprogram.LoadSSA()
|
||||
buildProgram := sync.OnceFunc(program.Build)
|
||||
|
||||
// Add jobs to compile each package.
|
||||
// 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
|
||||
}
|
||||
|
||||
// Compile AST to IR. The compiler.CompilePackage function will
|
||||
// build the SSA as needed.
|
||||
// SSA package builds may run concurrently, but the resulting
|
||||
// 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())
|
||||
defer mod.Context().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
|
||||
// bootloader loads, up to and including the appended SHA256 hash).
|
||||
ramImageSize := 0
|
||||
if makeImage {
|
||||
ramImageSize += 4096
|
||||
}
|
||||
ramImageSize += 24 // image header (8) + trailer fields (16)
|
||||
for _, seg := range segments {
|
||||
ramImageSize += 8 + len(seg.data) // segment header + data (4-aligned)
|
||||
@@ -284,6 +281,9 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
dromSize := 0
|
||||
if len(dromSegs) > 0 {
|
||||
targetImageOffset := int(dromFlashAddr - flashBase)
|
||||
if makeImage {
|
||||
targetImageOffset = int(dromFlashAddr)
|
||||
}
|
||||
if outf.Len() > targetImageOffset {
|
||||
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
|
||||
targetImageOffset := int(iromFlashAddr - flashBase)
|
||||
if makeImage {
|
||||
targetImageOffset = int(iromFlashAddr)
|
||||
}
|
||||
if outf.Len() > targetImageOffset {
|
||||
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.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 4313, 323, 0, 2260},
|
||||
{"microbit", "examples/serial", 2838, 382, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 8027, 1665, 132, 7488},
|
||||
{"hifive1b", "examples/echo", 4321, 323, 0, 2268},
|
||||
{"microbit", "examples/serial", 2842, 382, 8, 2264},
|
||||
{"wioterminal", "examples/pininterrupt", 8039, 1665, 132, 7496},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// 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.
|
||||
//
|
||||
// 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) {
|
||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||
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.program = ssaPkg.Prog
|
||||
|
||||
// Convert AST to SSA.
|
||||
ssaPkg.Build()
|
||||
|
||||
// Assign names to function-local named types before compiling the
|
||||
// package, so that types declared in different functions (or in
|
||||
// different instantiations of a generic function) do not collide.
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestCompiler(t *testing.T) {
|
||||
|
||||
// Update test if needed. Do not check the result.
|
||||
if *flagUpdate {
|
||||
err := os.WriteFile(outPath, []byte(mod.String()), 0666)
|
||||
err := os.WriteFile(outPath, []byte(normalizeIR(mod.String())), 0666)
|
||||
if err != nil {
|
||||
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
|
||||
// across LLVM versions. Golden files are written against LLVM <21; newer LLVM
|
||||
// prints some attributes differently.
|
||||
// normalizeIR canonicalizes LLVM-version-specific IR spellings for comparison
|
||||
// and when regenerating golden files.
|
||||
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
|
||||
@@ -414,5 +413,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string)
|
||||
// Compile AST to IR.
|
||||
program := lprogram.LoadSSA()
|
||||
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:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value)
|
||||
store i32 3, ptr %chan.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @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 @llvm.lifetime.end.p0(i64 16, 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.op)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
|
||||
declare void @llvm.lifetime.start.p0(ptr nocapture) #2
|
||||
|
||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
|
||||
declare void @llvm.lifetime.end.p0(ptr nocapture) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.op = alloca %runtime.channelOp, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value)
|
||||
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
|
||||
call void @llvm.lifetime.end.p0(i64 4, 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.value)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||
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 {
|
||||
entry:
|
||||
%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 @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -65,9 +65,9 @@ entry:
|
||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%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
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ entry:
|
||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||
%select.send.value = alloca i32, align 4
|
||||
store i32 1, ptr %select.send.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %select.states.alloca)
|
||||
store ptr %ch1, ptr %select.states.alloca, align 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
|
||||
@@ -86,7 +86,7 @@ entry:
|
||||
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
|
||||
store ptr null, ptr %.repack3, align 4
|
||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #3
|
||||
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
|
||||
%2 = icmp eq i32 %1, 0
|
||||
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: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
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
@@ -31,7 +31,7 @@ entry:
|
||||
}
|
||||
|
||||
; 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
|
||||
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
|
||||
|
||||
; 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
|
||||
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)
|
||||
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
|
||||
|
||||
; 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
|
||||
|
||||
|
||||
Vendored
+18
-18
@@ -21,23 +21,23 @@ entry:
|
||||
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
|
||||
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
||||
%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(i64 12, ptr nonnull %hashmap.key)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key)
|
||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||
%3 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
; 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
|
||||
|
||||
; 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
|
||||
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
|
||||
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
||||
%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
|
||||
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
|
||||
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(i64 4, ptr nonnull %hashmap.value)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -64,17 +64,17 @@ define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %m, [2
|
||||
entry:
|
||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||
%hashmap.value = alloca i32, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value)
|
||||
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key)
|
||||
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
||||
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||
%0 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
@@ -83,17 +83,17 @@ define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %m, [2
|
||||
entry:
|
||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||
%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
|
||||
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
|
||||
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
||||
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
|
||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||
call void @llvm.lifetime.end.p0(i64 24, 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.key)
|
||||
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value)
|
||||
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.
|
||||
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 {
|
||||
// Moving failed. Do a file copy.
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
emuCheck(t, options)
|
||||
|
||||
@@ -539,6 +554,9 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||
if config.EmulatorName() == "simavr" {
|
||||
actual = cleanSimAVRTestOutput(actual)
|
||||
}
|
||||
if config.EmulatorName() == "qemu-system-xtensa" {
|
||||
actual = cleanESP32QEMUOutput(actual)
|
||||
}
|
||||
if name == "testing.go" {
|
||||
// Strip actual time.
|
||||
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 {
|
||||
output = bytes.ReplaceAll(output, []byte{0x1b, '[', '3', '2', '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
|
||||
// has WindowStart bits set for the spill helper frames.
|
||||
// We will clear WindowStart completely (to 0) right before the retw.n
|
||||
// below, after the stack switch is done. This prevents stale overflow
|
||||
// when the new goroutine's calls rotate back into these panes.
|
||||
// We will drop every WindowStart bit except this window's own
|
||||
// (WINDOWSTART = 1 << WINDOWBASE) right before the retw.n below, after the
|
||||
// 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
|
||||
wsr.ps a4
|
||||
rsync
|
||||
|
||||
// At this point, the following is true:
|
||||
// WindowStart == 1 << WindowBase
|
||||
@@ -97,16 +103,24 @@ tinygo_swapTask:
|
||||
// register also stores the parent register window.
|
||||
l32i.n a0, sp, 0
|
||||
|
||||
// Clear ALL WindowStart bits. With all windows spilled to the stack,
|
||||
// we must ensure no stale WS bits remain: the retw.n below will trigger
|
||||
// underflow4 to load the new goroutine's registers from the new stack
|
||||
// (which sets the appropriate WS bit via rfwu). Any stale WS bits
|
||||
// (from spill helpers or the old goroutine) would cause spurious
|
||||
// overflows of garbage register values into memory.
|
||||
movi a5, 0
|
||||
// Drop every WindowStart bit except this window's. Stale bits (from the
|
||||
// spill helpers or the old goroutine) would overflow garbage registers into
|
||||
// memory. This window's own bit has to stay set: zeroing WindowStart marks
|
||||
// the running frame dead, so an interrupt arriving here returns through a
|
||||
// retw that underflows into a save area nothing ever wrote. Keeping it
|
||||
// costs nothing -- WindowStart[WindowBase - n] is still clear, so the
|
||||
// retw.n below underflows as intended.
|
||||
rsr a6, WINDOWBASE
|
||||
movi a5, 1
|
||||
ssl a6
|
||||
sll a5, a5 // a5 = 1 << WindowBase
|
||||
wsr a5, WINDOWSTART
|
||||
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
|
||||
// underflow, reloading the saved registers from the stack.
|
||||
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)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
dev.Attach()
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
@@ -68,6 +68,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
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() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// 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)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
dev.Attach()
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
@@ -71,6 +71,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
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() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// 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.
|
||||
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 {
|
||||
case PinOutput:
|
||||
// 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
|
||||
|
||||
// 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
|
||||
needAddress := true
|
||||
needRestart := false
|
||||
readLast := false
|
||||
var readTo []byte
|
||||
for cmdIdx, reg := 0, &i2c.Bus.COMD0; cmdIdx < len(cmd); {
|
||||
c := &cmd[cmdIdx]
|
||||
@@ -310,7 +309,6 @@ func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error {
|
||||
}
|
||||
|
||||
if split {
|
||||
readLast = true
|
||||
reg.Set(i2cCMD_READLAST | 1)
|
||||
reg = nextAddress(reg)
|
||||
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 {
|
||||
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
|
||||
case mask&esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk != 0:
|
||||
// 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) 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()
|
||||
// functions at compile time and cannot access hardware registers.
|
||||
// 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) 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()
|
||||
// functions at compile time and cannot access hardware registers.
|
||||
// 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) 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()
|
||||
// functions at compile time and cannot access hardware registers.
|
||||
// Actual hardware setup is deferred to the first Configure() call.
|
||||
|
||||
@@ -22,6 +22,11 @@ var (
|
||||
epinen uint32
|
||||
epouten uint32
|
||||
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
|
||||
@@ -89,6 +94,23 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
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) {
|
||||
if nrf.USBD.EVENTS_SOF.Get() == 1 {
|
||||
nrf.USBD.EVENTS_SOF.Set(0)
|
||||
@@ -103,7 +125,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
|
||||
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL)
|
||||
nrf.USBD.USBPULLUP.Set(1)
|
||||
if !usbDetached {
|
||||
nrf.USBD.USBPULLUP.Set(1)
|
||||
}
|
||||
|
||||
usbConfiguration = 0
|
||||
}
|
||||
|
||||
@@ -43,9 +43,24 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
rp.USBCTRL_REGS_INTE_SETUP_REQ)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
status := rp.USBCTRL_REGS.INTS.Get()
|
||||
|
||||
|
||||
@@ -43,12 +43,27 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
rp.USB_INTE_SETUP_REQ)
|
||||
|
||||
// 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
|
||||
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) {
|
||||
status := rp.USB.INTS.Get()
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
otgPower.PCGCCTL.Set(0)
|
||||
|
||||
// Soft-disconnect now (after CSRST reset DCTL to its default connected state).
|
||||
otgDevice.DCTL.SetBits(dctlSDIS)
|
||||
dev.Detach()
|
||||
|
||||
// ---- 7. Configure data FIFOs --------------------------------------------
|
||||
|
||||
@@ -311,11 +311,26 @@ func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
|
||||
// ---- 12. Connect to host (clear soft-disconnect) -----------------------
|
||||
|
||||
otgDevice.DCTL.ClearBits(dctlSDIS)
|
||||
dev.Attach()
|
||||
|
||||
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.
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
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
|
||||
// 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
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if ep == 0 {
|
||||
// Control endpoint
|
||||
|
||||
@@ -163,6 +163,17 @@ func (f *File) Truncate(size int64) (err error) {
|
||||
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 {
|
||||
if f.handle == nil {
|
||||
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
|
||||
|
||||
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) IsDir() bool { return fs.Mode().IsDir() }
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ var (
|
||||
endBlock gcBlock // the block just past the end of the available space
|
||||
gcTotalAlloc uint64 // total number of bytes allocated
|
||||
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
|
||||
)
|
||||
|
||||
@@ -510,7 +511,7 @@ func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
lastBlock := firstBlock.findHead()
|
||||
|
||||
// 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 {
|
||||
// The requested size is less than the old size.
|
||||
@@ -610,6 +611,11 @@ func runGC() (freeBytes uintptr) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -850,6 +856,9 @@ func ReadMemStats(m *MemStats) {
|
||||
// Record the total allocated bytes.
|
||||
m.TotalAlloc = gcTotalAlloc
|
||||
|
||||
// Record the number of completed collection cycles.
|
||||
m.NumGC = gcNumGC
|
||||
|
||||
gcLock.Unlock()
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@ func ReadMemStats(m *MemStats) {
|
||||
m.Mallocs = 0 // not provided by bdwgc
|
||||
m.Frees = 0 // not provided by bdwgc
|
||||
m.Sys = uint64(gcMemStats.obtained_from_os_bytes)
|
||||
m.NumGC = uint32(gcMemStats.gc_no)
|
||||
|
||||
gcLock.Unlock()
|
||||
}
|
||||
|
||||
@@ -47,8 +47,14 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
gcTotalAlloc += uint64(size)
|
||||
gcMallocs++
|
||||
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.
|
||||
// Use > instead of >= because an allocation that exactly
|
||||
// ends at heapEnd is still within heap boundaries.
|
||||
if growHeap() {
|
||||
continue
|
||||
}
|
||||
@@ -103,6 +109,7 @@ func ReadMemStats(m *MemStats) {
|
||||
m.HeapAlloc = gcTotalAlloc
|
||||
m.HeapObjects = gcMallocs
|
||||
m.Alloc = m.HeapAlloc
|
||||
m.NumGC = 0 // this GC never collects, so no cycle ever completes
|
||||
|
||||
gcLock.Unlock()
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ func gcMarkReachable() {
|
||||
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.
|
||||
scanCurrentStack()
|
||||
if !task.OnSystemStack() {
|
||||
@@ -53,13 +59,6 @@ func gcMarkReachable() {
|
||||
// Scan globals.
|
||||
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.
|
||||
for i := uint32(0); i < numCPU; i++ {
|
||||
if i == core {
|
||||
|
||||
@@ -807,13 +807,13 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
|
||||
case reflectlite.Array:
|
||||
var hash uint32
|
||||
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
|
||||
case reflectlite.Struct:
|
||||
var hash uint32
|
||||
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
|
||||
default:
|
||||
|
||||
@@ -82,4 +82,9 @@ type MemStats struct {
|
||||
|
||||
// GCSys is bytes of memory in garbage collection metadata.
|
||||
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
|
||||
var _vector_table [0]uintptr
|
||||
|
||||
func abort() {
|
||||
for {
|
||||
device.Asm("waiti 0")
|
||||
}
|
||||
}
|
||||
|
||||
// interruptInit installs the Xtensa vector table by writing its address
|
||||
// to the VECBASE special register and ensures all CPU interrupts are
|
||||
// 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
|
||||
}
|
||||
|
||||
func exit(code int) {
|
||||
abort()
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
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",
|
||||
"flash-method": "esp32flash",
|
||||
"emulator": "qemu-system-xtensa -machine esp32 -nographic -drive file={img},if=mtd,format=raw",
|
||||
"gdb": ["xtensa-esp32-elf-gdb"]
|
||||
}
|
||||
|
||||
@@ -299,13 +299,37 @@ _handle_kernel_exc:
|
||||
|
||||
.global _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):
|
||||
// 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
|
||||
// 48: a12 52: a13 56: a14 60: a15
|
||||
// 64: SAR 68: EPC1 72: PS
|
||||
addi a0, a1, -96 // a0 = new frame pointer
|
||||
// 64: SAR 68: EPC1 72: PS 76: WINDOWBASE 80: WINDOWSTART
|
||||
//
|
||||
// 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)
|
||||
mov a1, a0 // a1 = frame pointer
|
||||
|
||||
@@ -339,6 +363,15 @@ _handle_level1:
|
||||
// level-1 interrupts.
|
||||
rsr a2, PS
|
||||
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)
|
||||
and a2, a2, a3
|
||||
movi a3, 1 // INTLEVEL = 1
|
||||
@@ -412,6 +445,52 @@ _handle_level1:
|
||||
|
||||
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.
|
||||
// Overridden by the strong definition in espradio's isr.c when linked.
|
||||
|
||||
@@ -25,6 +25,5 @@
|
||||
],
|
||||
"binary-format": "esp32s3",
|
||||
"flash-method": "esp32jtag",
|
||||
"emulator": "qemu-system-xtensa -machine esp32 -nographic -drive file={img},if=mtd,format=raw",
|
||||
"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:]
|
||||
|
||||
if *update {
|
||||
err := os.WriteFile(pathPrefix+".out.ll", []byte(actual), 0666)
|
||||
err := os.WriteFile(pathPrefix+".out.ll", []byte(normalizeIR(actual)), 0666)
|
||||
if err != nil {
|
||||
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
|
||||
// etc.).
|
||||
func fuzzyEqualIR(s1, s2 string) bool {
|
||||
// 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 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")
|
||||
s1 = normalizeIR(s1)
|
||||
s2 = normalizeIR(s2)
|
||||
|
||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
||||
@@ -138,6 +118,29 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
||||
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
|
||||
// 'captures(none)', which is how LLVM 21+ orders these two attributes when
|
||||
// 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.
|
||||
program := lprogram.LoadSSA()
|
||||
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 {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
|
||||
Reference in New Issue
Block a user