mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 11:37:46 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88db2c3594 | |||
| fb2755d18f | |||
| 85d223c5e2 | |||
| 7b486e398a | |||
| da69f18e91 | |||
| 6971b8a69e | |||
| 6682e41ab4 | |||
| 18033ebc36 | |||
| 89d9e33bca | |||
| e80e7e5c10 | |||
| 6233915ecf | |||
| fd8e0bd70b | |||
| 45e2863e9f | |||
| 4204f3d065 | |||
| ddbd65beec | |||
| 1a1506ef79 | |||
| 8793dc37fa | |||
| e9d78a78ea | |||
| 7ec9cfc61c | |||
| 294cf28e43 | |||
| 2f7a66e6d3 |
@@ -1,119 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
commands:
|
|
||||||
submodules:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Pull submodules"
|
|
||||||
command: git submodule update --init
|
|
||||||
llvm-source-linux:
|
|
||||||
steps:
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- llvm-source-19-v1
|
|
||||||
- run:
|
|
||||||
name: "Fetch LLVM source"
|
|
||||||
command: make llvm-source
|
|
||||||
- save_cache:
|
|
||||||
key: llvm-source-19-v1
|
|
||||||
paths:
|
|
||||||
- llvm-project/clang/lib/Headers
|
|
||||||
- llvm-project/clang/include
|
|
||||||
- llvm-project/compiler-rt
|
|
||||||
- llvm-project/lld/include
|
|
||||||
- llvm-project/llvm/include
|
|
||||||
hack-ninja-jobs:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Hack Ninja to use less jobs"
|
|
||||||
command: |
|
|
||||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
|
||||||
chmod +x /go/bin/ninja
|
|
||||||
build-binaryen-linux:
|
|
||||||
steps:
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- binaryen-linux-v3
|
|
||||||
- run:
|
|
||||||
name: "Build Binaryen"
|
|
||||||
command: |
|
|
||||||
make binaryen
|
|
||||||
- save_cache:
|
|
||||||
key: binaryen-linux-v3
|
|
||||||
paths:
|
|
||||||
- build/wasm-opt
|
|
||||||
test-linux:
|
|
||||||
parameters:
|
|
||||||
llvm:
|
|
||||||
type: string
|
|
||||||
fmt-check:
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- submodules
|
|
||||||
- run:
|
|
||||||
name: "Install apt dependencies"
|
|
||||||
command: |
|
|
||||||
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
|
||||||
apt-get update
|
|
||||||
apt-get install --no-install-recommends -y \
|
|
||||||
llvm-<<parameters.llvm>>-dev \
|
|
||||||
clang-<<parameters.llvm>> \
|
|
||||||
libclang-<<parameters.llvm>>-dev \
|
|
||||||
lld-<<parameters.llvm>> \
|
|
||||||
cmake \
|
|
||||||
ninja-build
|
|
||||||
- hack-ninja-jobs
|
|
||||||
- build-binaryen-linux
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
|
||||||
- go-cache-v4-{{ checksum "go.mod" }}
|
|
||||||
- llvm-source-linux
|
|
||||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
|
||||||
- when:
|
|
||||||
condition: <<parameters.fmt-check>>
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
# Do this before gen-device so that it doesn't check the
|
|
||||||
# formatting of generated files.
|
|
||||||
name: Check Go code formatting
|
|
||||||
command: make fmt-check lint
|
|
||||||
- run: make gen-device -j4
|
|
||||||
# TODO: change this to -skip='TestErrors|TestWasm' with Go 1.20
|
|
||||||
- run: go test -tags=llvm<<parameters.llvm>> -short -run='TestBuild|TestTest|TestGetList|TestTraceback'
|
|
||||||
- run: make smoketest XTENSA=0
|
|
||||||
- save_cache:
|
|
||||||
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
|
||||||
paths:
|
|
||||||
- ~/.cache/go-build
|
|
||||||
- /go/pkg/mod
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test-oldest:
|
|
||||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
|
||||||
# least the smoke tests still pass.
|
|
||||||
docker:
|
|
||||||
- image: golang:1.23-bullseye
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "15"
|
|
||||||
resource_class: large
|
|
||||||
test-newest:
|
|
||||||
# This tests the latest supported LLVM version when linking against system
|
|
||||||
# libraries.
|
|
||||||
docker:
|
|
||||||
- image: golang:1.26-bookworm
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "20"
|
|
||||||
resource_class: large
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
test-all:
|
|
||||||
jobs:
|
|
||||||
- test-oldest
|
|
||||||
# disable this test, since CircleCI seems unable to download due to rate-limits on Dockerhub.
|
|
||||||
# - test-newest
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
build/
|
build/
|
||||||
llvm-*/
|
llvm-*/
|
||||||
.github
|
.github
|
||||||
.circleci
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# TinyGo - Go compiler for small places
|
# TinyGo - Go compiler for small places
|
||||||
|
|
||||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml)
|
||||||
|
|
||||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
||||||
|
|
||||||
|
|||||||
+14
-2
@@ -255,6 +255,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip default initializers for -X globals from the type info before
|
||||||
|
// building SSA. This prevents go/ssa from emitting init stores for them,
|
||||||
|
// so that makeGlobalsModule can supply the correct values at final link
|
||||||
|
// time without any runtime init overwriting them. The -X values themselves
|
||||||
|
// are kept out of the per-package build cache; only the variable names
|
||||||
|
// appear in the cache key.
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
for name := range globalValues[pkg.Pkg.Path()] {
|
||||||
|
pkg.StripVarInitializer(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
// 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()
|
||||||
@@ -477,7 +489,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if pkgInit.IsNil() {
|
if pkgInit.IsNil() {
|
||||||
panic("init not found for " + pkg.Pkg.Path())
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
}
|
}
|
||||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1196,7 +1208,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
|||||||
// needed to convert a program to its final form. Some transformations are not
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
// optional and must be run as the compiler expects them to run.
|
// optional and must be run as the compiler expects them to run.
|
||||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||||
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func RunTool(tool string, args ...string) error {
|
|||||||
var cflag *C.char
|
var cflag *C.char
|
||||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||||
defer C.free(buf)
|
defer C.free(buf)
|
||||||
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):len(args)]
|
cflags := unsafe.Slice((**C.char)(buf), len(args))
|
||||||
for i, flag := range args {
|
for i, flag := range args {
|
||||||
cflag := C.CString(flag)
|
cflag := C.CString(flag)
|
||||||
cflags[i] = cflag
|
cflags[i] = cflag
|
||||||
|
|||||||
+5
-1
@@ -122,12 +122,16 @@ func parseLLDErrors(text string) error {
|
|||||||
parsedError = true
|
parsedError = true
|
||||||
line, _ := strconv.Atoi(matches[3])
|
line, _ := strconv.Atoi(matches[3])
|
||||||
// TODO: detect common mistakes like -gc=none?
|
// TODO: detect common mistakes like -gc=none?
|
||||||
|
msg := "linker could not find symbol " + symbolName
|
||||||
|
if symbolName == "runtime.alloc_noheap" {
|
||||||
|
msg = "object allocated on the heap in //go:noheap function"
|
||||||
|
}
|
||||||
linkErrors = append(linkErrors, scanner.Error{
|
linkErrors = append(linkErrors, scanner.Error{
|
||||||
Pos: token.Position{
|
Pos: token.Position{
|
||||||
Filename: matches[2],
|
Filename: matches[2],
|
||||||
Line: line,
|
Line: line,
|
||||||
},
|
},
|
||||||
Msg: "linker could not find symbol " + symbolName,
|
Msg: msg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -130,7 +130,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
|||||||
// convert Go slice of strings to C array of strings.
|
// convert Go slice of strings to C array of strings.
|
||||||
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
||||||
defer C.free(cmdargsC)
|
defer C.free(cmdargsC)
|
||||||
cmdargs := (*[1 << 16]*C.char)(cmdargsC)
|
cmdargs := unsafe.Slice((**C.char)(cmdargsC), len(cflags))
|
||||||
for i, cflag := range cflags {
|
for i, cflag := range cflags {
|
||||||
s := C.CString(cflag)
|
s := C.CString(cflag)
|
||||||
cmdargs[i] = s
|
cmdargs[i] = s
|
||||||
@@ -190,7 +190,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
|||||||
// Sanity check. This should (hopefully) never trigger.
|
// Sanity check. This should (hopefully) never trigger.
|
||||||
panic("libclang: file contents was not loaded")
|
panic("libclang: file contents was not loaded")
|
||||||
}
|
}
|
||||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
data := unsafe.Slice((*byte)(unsafe.Pointer(rawData)), size)
|
||||||
|
|
||||||
// Hash the contents if it isn't hashed yet.
|
// Hash the contents if it isn't hashed yet.
|
||||||
if _, ok := f.visitedFiles[path]; !ok {
|
if _, ok := f.visitedFiles[path]; !ok {
|
||||||
@@ -624,7 +624,7 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
|||||||
// now by reading the file from libclang.
|
// now by reading the file from libclang.
|
||||||
var size C.size_t
|
var size C.size_t
|
||||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size]
|
source := unsafe.Slice((*byte)(unsafe.Pointer(sourcePtr)), size)
|
||||||
lines := []int{0}
|
lines := []int{0}
|
||||||
for i := 0; i < len(source)-1; i++ {
|
for i := 0; i < len(source)-1; i++ {
|
||||||
if source[i] == '\n' {
|
if source[i] == '\n' {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include
|
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include -I/usr/lib64/llvm19/include
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include
|
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include -I/usr/lib64/llvm20/include
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
||||||
|
|||||||
+40
-39
@@ -21,45 +21,46 @@ var (
|
|||||||
// usually passed from the command line, but can also be passed in environment
|
// usually passed from the command line, but can also be passed in environment
|
||||||
// variables for example.
|
// variables for example.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
GOOS string // environment variable
|
GOOS string // environment variable
|
||||||
GOARCH string // environment variable
|
GOARCH string // environment variable
|
||||||
GOARM string // environment variable (only used with GOARCH=arm)
|
GOARM string // environment variable (only used with GOARCH=arm)
|
||||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
||||||
Directory string // working dir, leave it unset to use the current working dir
|
Directory string // working dir, leave it unset to use the current working dir
|
||||||
Target string
|
Target string
|
||||||
BuildMode string // -buildmode flag
|
BuildMode string // -buildmode flag
|
||||||
Opt string
|
Opt string
|
||||||
GC string
|
GC string
|
||||||
PanicStrategy string
|
PanicStrategy string
|
||||||
Scheduler string
|
Scheduler string
|
||||||
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
||||||
Serial string
|
Serial string
|
||||||
Work bool // -work flag to print temporary build directory
|
Work bool // -work flag to print temporary build directory
|
||||||
InterpTimeout time.Duration
|
InterpTimeout time.Duration
|
||||||
PrintIR bool
|
InterpMaxLoopIterations int
|
||||||
DumpSSA bool
|
PrintIR bool
|
||||||
VerifyIR bool
|
DumpSSA bool
|
||||||
SkipDWARF bool
|
VerifyIR bool
|
||||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
SkipDWARF bool
|
||||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||||
Debug bool
|
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||||
Nobounds bool
|
Debug bool
|
||||||
PrintSizes string
|
Nobounds bool
|
||||||
PrintAllocs *regexp.Regexp // regexp string
|
PrintSizes string
|
||||||
PrintStacks bool
|
PrintAllocs *regexp.Regexp // regexp string
|
||||||
Tags []string
|
PrintStacks bool
|
||||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
Tags []string
|
||||||
TestConfig TestConfig
|
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||||
Programmer string
|
TestConfig TestConfig
|
||||||
OpenOCDCommands []string
|
Programmer string
|
||||||
LLVMFeatures string
|
OpenOCDCommands []string
|
||||||
Monitor bool
|
LLVMFeatures string
|
||||||
BaudRate int
|
Monitor bool
|
||||||
Timeout time.Duration
|
BaudRate int
|
||||||
WITPackage string // pass through to wasm-tools component embed invocation
|
Timeout time.Duration
|
||||||
WITWorld string // pass through to wasm-tools component embed -w option
|
WITPackage string // pass through to wasm-tools component embed invocation
|
||||||
ExtLDFlags []string
|
WITWorld string // pass through to wasm-tools component embed -w option
|
||||||
GoCompatibility bool // enable to check for Go version compatibility
|
ExtLDFlags []string
|
||||||
|
GoCompatibility bool // enable to check for Go version compatibility
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||||
|
|||||||
+12
-5
@@ -192,6 +192,16 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the runtime.alloc function variant.
|
||||||
|
// This is normally just "alloc", but is "alloc_noheap" if the //go:noheap
|
||||||
|
// pragma is used.
|
||||||
|
func (b *builder) allocFunc() string {
|
||||||
|
if b.info.noheap {
|
||||||
|
return "alloc_noheap"
|
||||||
|
}
|
||||||
|
return "alloc"
|
||||||
|
}
|
||||||
|
|
||||||
type blockInfo struct {
|
type blockInfo struct {
|
||||||
// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
|
// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
|
||||||
entry llvm.BasicBlock
|
entry llvm.BasicBlock
|
||||||
@@ -314,9 +324,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
// Load comments such as //go:extern on globals.
|
// Load comments such as //go:extern on globals.
|
||||||
c.loadASTComments(pkg)
|
c.loadASTComments(pkg)
|
||||||
|
|
||||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
|
||||||
// functionality.
|
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
|
||||||
if c.NeedsStackObjects {
|
if c.NeedsStackObjects {
|
||||||
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
|
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
|
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
|
||||||
@@ -2176,7 +2183,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
||||||
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
buf := b.createRuntimeCall(b.allocFunc(), []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
||||||
align := b.targetData.ABITypeAlignment(typ)
|
align := b.targetData.ABITypeAlignment(typ)
|
||||||
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||||
return buf, nil
|
return buf, nil
|
||||||
@@ -2408,7 +2415,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
||||||
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
||||||
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
slicePtr := b.createRuntimeCall(b.allocFunc(), []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
||||||
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
|
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
|
||||||
|
|
||||||
// Extend or truncate if necessary. This is safe as we've already done
|
// Extend or truncate if necessary. This is safe as we've already done
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
{"channel.go", "", ""},
|
{"channel.go", "", ""},
|
||||||
{"gc.go", "", ""},
|
{"gc.go", "", ""},
|
||||||
{"zeromap.go", "", ""},
|
{"zeromap.go", "", ""},
|
||||||
|
{"generics.go", "", ""},
|
||||||
}
|
}
|
||||||
if goMinor >= 20 {
|
if goMinor >= 20 {
|
||||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||||
|
|||||||
+1
-1
@@ -488,7 +488,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
nilPtr := llvm.ConstNull(b.dataPtrType)
|
nilPtr := llvm.ConstNull(b.dataPtrType)
|
||||||
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
alloca = b.createRuntimeCall(b.allocFunc(), []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||||
}
|
}
|
||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
b.trackPointer(alloca)
|
b.trackPointer(alloca)
|
||||||
|
|||||||
+1
-3
@@ -129,11 +129,9 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
align := b.targetData.ABITypeAlignment(packedType)
|
align := b.targetData.ABITypeAlignment(packedType)
|
||||||
alloc := b.mod.NamedFunction("runtime.alloc")
|
packedAlloc := b.createRuntimeCall(b.allocFunc(), []llvm.Value{
|
||||||
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
|
||||||
sizeValue,
|
sizeValue,
|
||||||
llvm.ConstNull(b.dataPtrType),
|
llvm.ConstNull(b.dataPtrType),
|
||||||
llvm.Undef(b.dataPtrType), // unused context parameter
|
|
||||||
}, "")
|
}, "")
|
||||||
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
|
|||||||
+454
-167
@@ -3,42 +3,28 @@ package compiler
|
|||||||
// This file emits the correct map intrinsics for map operations.
|
// This file emits the correct map intrinsics for map operations.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/src/tinygo"
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const hashArrayUnrollLimit = 4
|
||||||
|
|
||||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||||
// initializing an appropriately sized object.
|
// initializing an appropriately sized object.
|
||||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||||
mapType := expr.Type().Underlying().(*types.Map)
|
mapType := expr.Type().Underlying().(*types.Map)
|
||||||
keyType := mapType.Key().Underlying()
|
keyType := mapType.Key().Underlying()
|
||||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||||
var llvmKeyType llvm.Type
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
var alg uint64
|
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
|
||||||
// String keys.
|
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
|
||||||
alg = uint64(tinygo.HashmapAlgorithmString)
|
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
|
||||||
// Trivially comparable keys.
|
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
|
||||||
alg = uint64(tinygo.HashmapAlgorithmBinary)
|
|
||||||
} else {
|
|
||||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
|
||||||
// implementation.
|
|
||||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
|
||||||
alg = uint64(tinygo.HashmapAlgorithmInterface)
|
|
||||||
}
|
|
||||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
|
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
|
||||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||||
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
|
||||||
if expr.Reserve != nil {
|
if expr.Reserve != nil {
|
||||||
sizeHint = b.getValue(expr.Reserve, getPos(expr))
|
sizeHint = b.getValue(expr.Reserve, getPos(expr))
|
||||||
var err error
|
var err error
|
||||||
@@ -47,10 +33,42 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
|
||||||
|
// Resolve hash and equal functions for this key type. For string and
|
||||||
|
// binary key types, reference the corresponding runtime functions
|
||||||
|
// directly. For composite types, generate type-specific functions.
|
||||||
|
var hashFn, equalFn llvm.Value
|
||||||
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
|
hashFn = b.getRuntimeFunctionValue("hashmapStringPtrHash", hashmapKeyHashSignature())
|
||||||
|
equalFn = b.getRuntimeFunctionValue("hashmapStringEqual", hashmapKeyEqualSignature())
|
||||||
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
|
hashFn = b.getRuntimeFunctionValue("hash32", hashmapKeyHashSignature())
|
||||||
|
equalFn = b.getRuntimeFunctionValue("memequal", hashmapKeyEqualSignature())
|
||||||
|
} else {
|
||||||
|
fn := b.getOrGenerateKeyHashFunc(keyType)
|
||||||
|
hashFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyHashSignature())
|
||||||
|
fn = b.getOrGenerateKeyEqualFunc(keyType)
|
||||||
|
equalFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyEqualSignature())
|
||||||
|
}
|
||||||
|
|
||||||
|
hashmap := b.createRuntimeCall("hashmapMakeGeneric", []llvm.Value{
|
||||||
|
llvmKeySize, llvmValueSize, sizeHint,
|
||||||
|
hashFn, equalFn,
|
||||||
|
}, "")
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRuntimeFunctionValue returns a TinyGo function value (with nil context)
|
||||||
|
// for the named runtime function.
|
||||||
|
func (b *builder) getRuntimeFunctionValue(name string, sig *types.Signature) llvm.Value {
|
||||||
|
member := b.program.ImportedPackage("runtime").Members[name]
|
||||||
|
if member == nil {
|
||||||
|
panic("unknown runtime function: " + name)
|
||||||
|
}
|
||||||
|
_, llvmFn := b.getFunction(member.(*ssa.Function))
|
||||||
|
return b.createFuncValue(llvmFn, llvm.ConstNull(b.dataPtrType), sig)
|
||||||
|
}
|
||||||
|
|
||||||
// createMapLookup returns the value in a map. It calls a runtime function
|
// createMapLookup returns the value in a map. It calls a runtime function
|
||||||
// depending on the map key type to load the map value and its comma-ok value.
|
// depending on the map key type to load the map value and its comma-ok value.
|
||||||
func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||||
@@ -72,32 +90,23 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
|
|||||||
|
|
||||||
// Do the lookup. How it is done depends on the key type.
|
// Do the lookup. How it is done depends on the key type.
|
||||||
var commaOkValue llvm.Value
|
var commaOkValue llvm.Value
|
||||||
origKeyType := keyType
|
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
|
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
|
||||||
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else {
|
||||||
// key can be compared with runtime.memequal
|
// Key stored at actual type: either binary-comparable or with
|
||||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
// compiler-generated hash/equal.
|
||||||
// growth.
|
|
||||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, mapKeyAlloca)
|
b.CreateStore(key, mapKeyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
|
|
||||||
// Fetch the value from the hashmap.
|
|
||||||
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
|
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
|
||||||
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
fnName := "hashmapBinaryGet"
|
||||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
if !hashmapIsBinaryKey(keyType) {
|
||||||
} else {
|
fnName = "hashmapGenericGet"
|
||||||
// Not trivially comparable using memcmp. Make it an interface instead.
|
|
||||||
itfKey := key
|
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
|
||||||
// Not already an interface, so convert it to an interface now.
|
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
|
commaOkValue = b.createRuntimeCall(fnName, params, "")
|
||||||
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
|
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the resulting value from the hashmap. The value is set to the zero
|
// Load the resulting value from the hashmap. The value is set to the zero
|
||||||
@@ -120,29 +129,22 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
|
|||||||
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||||
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
||||||
b.CreateStore(value, valueAlloca)
|
b.CreateStore(value, valueAlloca)
|
||||||
origKeyType := keyType
|
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, valueAlloca}
|
params := []llvm.Value{m, key, valueAlloca}
|
||||||
b.createRuntimeCall("hashmapStringSet", params, "")
|
b.createRuntimeCall("hashmapStringSet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else {
|
||||||
// key can be compared with runtime.memequal
|
// Key stored at actual type.
|
||||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
fnName := "hashmapBinarySet"
|
||||||
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
if !hashmapIsBinaryKey(keyType) {
|
||||||
b.createRuntimeCall("hashmapBinarySet", params, "")
|
fnName = "hashmapGenericSet"
|
||||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
|
||||||
} else {
|
|
||||||
// Key is not trivially comparable, so compare it as an interface instead.
|
|
||||||
itfKey := key
|
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
|
||||||
// Not already an interface, so convert it to an interface first.
|
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, valueAlloca}
|
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
||||||
b.createRuntimeCall("hashmapInterfaceSet", params, "")
|
b.createRuntimeCall(fnName, params, "")
|
||||||
|
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||||
}
|
}
|
||||||
b.emitLifetimeEnd(valueAlloca, valueSize)
|
b.emitLifetimeEnd(valueAlloca, valueSize)
|
||||||
}
|
}
|
||||||
@@ -150,31 +152,23 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
|
|||||||
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
||||||
// function. It is the implementation of the Go delete() builtin.
|
// function. It is the implementation of the Go delete() builtin.
|
||||||
func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||||
origKeyType := keyType
|
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key}
|
params := []llvm.Value{m, key}
|
||||||
b.createRuntimeCall("hashmapStringDelete", params, "")
|
b.createRuntimeCall("hashmapStringDelete", params, "")
|
||||||
return nil
|
return nil
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else {
|
||||||
|
// Key stored at actual type.
|
||||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
fnName := "hashmapBinaryDelete"
|
||||||
params := []llvm.Value{m, keyAlloca}
|
if !hashmapIsBinaryKey(keyType) {
|
||||||
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
fnName = "hashmapGenericDelete"
|
||||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
// Key is not trivially comparable, so compare it as an interface
|
|
||||||
// instead.
|
|
||||||
itfKey := key
|
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
|
||||||
// Not already an interface, so convert it to an interface first.
|
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey}
|
params := []llvm.Value{m, keyAlloca}
|
||||||
b.createRuntimeCall("hashmapInterfaceDelete", params, "")
|
b.createRuntimeCall(fnName, params, "")
|
||||||
|
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,42 +189,15 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
llvmKeyType := b.getLLVMType(keyType)
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
llvmValueType := b.getLLVMType(valueType)
|
llvmValueType := b.getLLVMType(valueType)
|
||||||
|
|
||||||
// There is a special case in which keys are stored as an interface value
|
// All key types are now stored at their declared type (no interface wrapping).
|
||||||
// instead of the value they normally are. This happens for non-trivially
|
|
||||||
// comparable types such as float32 or some structs.
|
|
||||||
isKeyStoredAsInterface := false
|
|
||||||
if t, ok := keyType.Underlying().(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
|
||||||
// key is a string
|
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
|
||||||
// key can be compared with runtime.memequal
|
|
||||||
} else {
|
|
||||||
// The key is stored as an interface value, and may or may not be an
|
|
||||||
// interface type (for example, float32 keys are stored as an interface
|
|
||||||
// value).
|
|
||||||
if _, ok := keyType.Underlying().(*types.Interface); !ok {
|
|
||||||
isKeyStoredAsInterface = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the type of the key as stored in the map.
|
|
||||||
llvmStoredKeyType := llvmKeyType
|
|
||||||
if isKeyStoredAsInterface {
|
|
||||||
llvmStoredKeyType = b.getLLVMRuntimeType("_interface")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the key and value from the map.
|
// Extract the key and value from the map.
|
||||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmKeyType, "range.key")
|
||||||
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
||||||
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
|
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
|
||||||
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
mapKey := b.CreateLoad(llvmKeyType, mapKeyAlloca, "")
|
||||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||||
|
|
||||||
if isKeyStoredAsInterface {
|
|
||||||
// The key is stored as an interface but it isn't of interface type.
|
|
||||||
// Extract the underlying value.
|
|
||||||
mapKey = b.extractValueFromInterface(mapKey, llvmKeyType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// End the lifetimes of the allocas, because we're done with them.
|
// End the lifetimes of the allocas, because we're done with them.
|
||||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||||
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
|
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
|
||||||
@@ -250,20 +217,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||||
switch keyType := keyType.Underlying().(type) {
|
switch keyType := keyType.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
// TODO: unsafe.Pointer is also a binary key, but to support that we
|
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 || keyType.Kind() == types.UnsafePointer
|
||||||
// need to fix an issue with interp first (see
|
|
||||||
// https://github.com/tinygo-org/tinygo/pull/4898).
|
|
||||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
return true
|
return true
|
||||||
case *types.Struct:
|
|
||||||
for i := 0; i < keyType.NumFields(); i++ {
|
|
||||||
fieldType := keyType.Field(i).Type().Underlying()
|
|
||||||
if !hashmapIsBinaryKey(fieldType) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
return hashmapIsBinaryKey(keyType.Elem())
|
return hashmapIsBinaryKey(keyType.Elem())
|
||||||
default:
|
default:
|
||||||
@@ -271,68 +227,399 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
|
// hashmapKeyHashSignature returns the Go type signature for hashmap key hash
|
||||||
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
|
// functions: func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||||
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
|
func hashmapKeyHashSignature() *types.Signature {
|
||||||
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
|
return types.NewSignatureType(nil, nil, nil,
|
||||||
// we handle nested types. Next, we determine if there are any padding bytes before the next
|
types.NewTuple(
|
||||||
// element and zero those as well.
|
types.NewVar(token.NoPos, nil, "key", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uintptr]),
|
||||||
|
types.NewVar(token.NoPos, nil, "seed", types.Typ[types.Uintptr]),
|
||||||
|
),
|
||||||
|
types.NewTuple(
|
||||||
|
types.NewVar(token.NoPos, nil, "", types.Typ[types.Uint32]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
// hashmapKeyEqualSignature returns the Go type signature for hashmap key equal
|
||||||
|
// functions: func(x, y unsafe.Pointer, n uintptr) bool
|
||||||
|
func hashmapKeyEqualSignature() *types.Signature {
|
||||||
|
return types.NewSignatureType(nil, nil, nil,
|
||||||
|
types.NewTuple(
|
||||||
|
types.NewVar(token.NoPos, nil, "x", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "y", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "n", types.Typ[types.Uintptr]),
|
||||||
|
),
|
||||||
|
types.NewTuple(
|
||||||
|
types.NewVar(token.NoPos, nil, "", types.Typ[types.Bool]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
switch llvmType.TypeKind() {
|
// hashmapKeyFuncName returns a canonical name for a generated hash or equal
|
||||||
case llvm.IntegerTypeKind:
|
// function based on the key type's underlying structure. Named types are
|
||||||
// no padding bytes
|
// replaced with their underlying types so that structurally identical key
|
||||||
return nil
|
// types (e.g., struct{i1; str1} and struct{i2; str2} where both i1, i2 are
|
||||||
case llvm.PointerTypeKind:
|
// int and str1, str2 are string) share the same generated function.
|
||||||
// mo padding bytes
|
func hashmapKeyFuncName(prefix string, keyType types.Type) string {
|
||||||
return nil
|
return prefix + "." + hashmapCanonicalTypeName(keyType)
|
||||||
case llvm.ArrayTypeKind:
|
}
|
||||||
llvmArrayType := llvmType
|
|
||||||
llvmElemType := llvmType.ElementType()
|
|
||||||
|
|
||||||
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
|
// hashmapCanonicalTypeName returns a string representation of the hash/equal
|
||||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
// operations needed for a type, stripping named types where the operation does
|
||||||
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
|
// not depend on the name. Pointer and channel names do not include the element
|
||||||
|
// type because their hash/equal operations only use the pointer word.
|
||||||
// zero any padding bytes in this element
|
func hashmapCanonicalTypeName(t types.Type) string {
|
||||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
switch t := t.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
return t.Name()
|
||||||
|
case *types.Pointer:
|
||||||
|
return "*"
|
||||||
|
case *types.Chan:
|
||||||
|
switch t.Dir() {
|
||||||
|
case types.SendRecv:
|
||||||
|
return "chan"
|
||||||
|
case types.SendOnly:
|
||||||
|
return "chan<-"
|
||||||
|
case types.RecvOnly:
|
||||||
|
return "<-chan"
|
||||||
}
|
}
|
||||||
|
case *types.Interface:
|
||||||
case llvm.StructTypeKind:
|
if t.NumMethods() == 0 {
|
||||||
llvmStructType := llvmType
|
return "interface{}"
|
||||||
numFields := llvmStructType.StructElementTypesCount()
|
|
||||||
llvmElementTypes := llvmStructType.StructElementTypes()
|
|
||||||
|
|
||||||
for i := 0; i < numFields; i++ {
|
|
||||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
|
||||||
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
|
|
||||||
|
|
||||||
// zero any padding bytes in this field
|
|
||||||
llvmElemType := llvmElementTypes[i]
|
|
||||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
|
||||||
|
|
||||||
// zero any padding bytes before the next field, if any
|
|
||||||
offset := b.targetData.ElementOffset(llvmStructType, i)
|
|
||||||
storeSize := b.targetData.TypeStoreSize(llvmElemType)
|
|
||||||
fieldEndOffset := offset + storeSize
|
|
||||||
|
|
||||||
var nextOffset uint64
|
|
||||||
if i < numFields-1 {
|
|
||||||
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
|
|
||||||
} else {
|
|
||||||
// Last field? Next offset is the total size of the allocate struct.
|
|
||||||
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fieldEndOffset != nextOffset {
|
|
||||||
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
|
|
||||||
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
|
|
||||||
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
|
|
||||||
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return t.String()
|
||||||
|
case *types.Struct:
|
||||||
|
s := "struct{"
|
||||||
|
for i := 0; i < t.NumFields(); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
s += "; "
|
||||||
|
}
|
||||||
|
s += hashmapCanonicalTypeName(t.Field(i).Type())
|
||||||
|
}
|
||||||
|
return s + "}"
|
||||||
|
case *types.Array:
|
||||||
|
return fmt.Sprintf("[%d]%s", t.Len(), hashmapCanonicalTypeName(t.Elem()))
|
||||||
|
}
|
||||||
|
return t.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrGenerateKeyHashFunc returns an LLVM function that computes the hash
|
||||||
|
// of a key of the given type. The function is generated on first call and
|
||||||
|
// cached in the module.
|
||||||
|
func (b *builder) getOrGenerateKeyHashFunc(keyType types.Type) llvm.Value {
|
||||||
|
name := hashmapKeyFuncName("hashmapKeyHash", keyType)
|
||||||
|
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||||
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
// Create the LLVM function type:
|
||||||
|
// (key ptr, size uintptr, seed uintptr, context ptr) -> i32
|
||||||
|
fnType := llvm.FunctionType(b.ctx.Int32Type(), []llvm.Type{
|
||||||
|
b.dataPtrType, b.uintptrType, b.uintptrType, b.dataPtrType,
|
||||||
|
}, false)
|
||||||
|
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||||
|
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
fn.SetUnnamedAddr(true)
|
||||||
|
b.addStandardAttributes(fn)
|
||||||
|
|
||||||
|
// Generate the function body.
|
||||||
|
savedBlock := b.GetInsertBlock()
|
||||||
|
defer b.SetInsertPointAtEnd(savedBlock)
|
||||||
|
|
||||||
|
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||||
|
b.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
|
keyPtr := fn.Param(0)
|
||||||
|
seed := fn.Param(2)
|
||||||
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
|
hash := b.generateKeyHash(keyType, llvmKeyType, keyPtr, seed)
|
||||||
|
b.CreateRet(hash)
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrGenerateKeyEqualFunc returns an LLVM function that compares two keys
|
||||||
|
// of the given type for equality. The function is generated on first call
|
||||||
|
// and cached in the module.
|
||||||
|
func (b *builder) getOrGenerateKeyEqualFunc(keyType types.Type) llvm.Value {
|
||||||
|
name := hashmapKeyFuncName("hashmapKeyEqual", keyType)
|
||||||
|
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the LLVM function type:
|
||||||
|
// (x ptr, y ptr, n uintptr, context ptr) -> i1
|
||||||
|
fnType := llvm.FunctionType(b.ctx.Int1Type(), []llvm.Type{
|
||||||
|
b.dataPtrType, b.dataPtrType, b.uintptrType, b.dataPtrType,
|
||||||
|
}, false)
|
||||||
|
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||||
|
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
fn.SetUnnamedAddr(true)
|
||||||
|
b.addStandardAttributes(fn)
|
||||||
|
|
||||||
|
// Generate the function body.
|
||||||
|
savedBlock := b.GetInsertBlock()
|
||||||
|
defer b.SetInsertPointAtEnd(savedBlock)
|
||||||
|
|
||||||
|
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||||
|
b.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
|
xPtr := fn.Param(0)
|
||||||
|
yPtr := fn.Param(1)
|
||||||
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
|
result := b.generateKeyEqual(keyType, llvmKeyType, xPtr, yPtr, fn)
|
||||||
|
b.CreateRet(result)
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateKeyHash generates IR that hashes a key value. Returns the i32 hash.
|
||||||
|
func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, keyPtr llvm.Value, seed llvm.Value) llvm.Value {
|
||||||
|
switch keyType := keyType.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
if keyType.Info()&types.IsString != 0 {
|
||||||
|
// Hash the string contents. The size parameter is unused by
|
||||||
|
// hashmapStringPtrHash (it dereferences the string header to
|
||||||
|
// get the actual length), but we pass it for signature
|
||||||
|
// consistency with other hash functions.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hashmapStringPtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsFloat != 0 {
|
||||||
|
// Float hash: normalizes -0 to +0 before hashing.
|
||||||
|
if keyType.Kind() == types.Float32 {
|
||||||
|
return b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||||
|
}
|
||||||
|
return b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsComplex != 0 {
|
||||||
|
// Complex hash: hash real and imaginary parts as floats.
|
||||||
|
if keyType.Kind() == types.Complex64 {
|
||||||
|
realPtr := keyPtr
|
||||||
|
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||||
|
llvm.ConstInt(b.uintptrType, 4, false),
|
||||||
|
}, "")
|
||||||
|
realHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||||
|
imagHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||||
|
return b.CreateXor(realHash, imagHash, "")
|
||||||
|
}
|
||||||
|
realPtr := keyPtr
|
||||||
|
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||||
|
llvm.ConstInt(b.uintptrType, 8, false),
|
||||||
|
}, "")
|
||||||
|
realHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||||
|
imagHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||||
|
return b.CreateXor(realHash, imagHash, "")
|
||||||
|
}
|
||||||
|
// Integer/boolean: hash the raw bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
case *types.Pointer, *types.Chan:
|
||||||
|
// Pointers and channels: hash as raw pointer-sized bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
case *types.Interface:
|
||||||
|
// Interface: use runtime reflection-based hash.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hashmapInterfacePtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
case *types.Struct:
|
||||||
|
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < keyType.NumFields(); i++ {
|
||||||
|
if keyType.Field(i).Name() == "_" {
|
||||||
|
continue // blank fields are ignored in Go equality
|
||||||
|
}
|
||||||
|
fieldType := keyType.Field(i).Type()
|
||||||
|
llvmFieldType := b.getLLVMType(fieldType)
|
||||||
|
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||||
|
continue // skip zero-sized fields
|
||||||
|
}
|
||||||
|
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||||
|
fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
fieldHash := b.generateKeyHash(fieldType, llvmFieldType, fieldPtr, seed)
|
||||||
|
hash = b.CreateXor(hash, fieldHash, "")
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
case *types.Array:
|
||||||
|
elemType := keyType.Elem()
|
||||||
|
llvmElemType := b.getLLVMType(elemType)
|
||||||
|
arrayLen := keyType.Len()
|
||||||
|
if hashmapIsBinaryKey(elemType) {
|
||||||
|
// All elements are binary-comparable; hash the entire array as raw bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
}
|
||||||
|
if arrayLen == 0 {
|
||||||
|
return llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
}
|
||||||
|
if arrayLen <= hashArrayUnrollLimit {
|
||||||
|
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < int(arrayLen); i++ {
|
||||||
|
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||||
|
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||||
|
hash = b.CreateXor(hash, elemHash, "")
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
initHash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
|
||||||
|
loopEntry := b.GetInsertBlock()
|
||||||
|
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.body")
|
||||||
|
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.done")
|
||||||
|
|
||||||
|
b.CreateBr(loopBody)
|
||||||
|
b.SetInsertPointAtEnd(loopBody)
|
||||||
|
|
||||||
|
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||||
|
phiHash := b.CreatePHI(b.ctx.Int32Type(), "hash.acc")
|
||||||
|
|
||||||
|
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, phiI}, "")
|
||||||
|
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||||
|
newHash := b.CreateXor(phiHash, elemHash, "")
|
||||||
|
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||||
|
cond := b.CreateICmp(llvm.IntULT, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||||
|
b.CreateCondBr(cond, loopBody, loopDone)
|
||||||
|
|
||||||
|
bodyEnd := b.GetInsertBlock()
|
||||||
|
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||||
|
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||||
|
phiHash.AddIncoming([]llvm.Value{initHash, newHash},
|
||||||
|
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||||
|
|
||||||
|
b.SetInsertPointAtEnd(loopDone)
|
||||||
|
return newHash
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled key type for hash generation: %T", keyType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateKeyEqual generates IR that compares two key values for equality.
|
||||||
|
// Returns an i1 result.
|
||||||
|
func (b *builder) generateKeyEqual(keyType types.Type, llvmKeyType llvm.Type, xPtr, yPtr llvm.Value, fn llvm.Value) llvm.Value {
|
||||||
|
switch keyType := keyType.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
if keyType.Info()&types.IsString != 0 {
|
||||||
|
// Compare strings: load both string headers and compare.
|
||||||
|
xStr := b.CreateLoad(llvmKeyType, xPtr, "x.str")
|
||||||
|
yStr := b.CreateLoad(llvmKeyType, yPtr, "y.str")
|
||||||
|
return b.createRuntimeCall("stringEqual", []llvm.Value{xStr, yStr}, "eq")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsFloat != 0 {
|
||||||
|
// Float equality: fcmp oeq handles -0==+0 (true) and NaN==NaN (false).
|
||||||
|
xVal := b.CreateLoad(llvmKeyType, xPtr, "x.float")
|
||||||
|
yVal := b.CreateLoad(llvmKeyType, yPtr, "y.float")
|
||||||
|
return b.CreateFCmp(llvm.FloatOEQ, xVal, yVal, "eq")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsComplex != 0 {
|
||||||
|
// Complex equality: both real and imaginary parts must be equal.
|
||||||
|
var floatType llvm.Type
|
||||||
|
if keyType.Kind() == types.Complex64 {
|
||||||
|
floatType = b.ctx.FloatType()
|
||||||
|
} else {
|
||||||
|
floatType = b.ctx.DoubleType()
|
||||||
|
}
|
||||||
|
floatSize := b.targetData.TypeAllocSize(floatType)
|
||||||
|
imagOffset := llvm.ConstInt(b.uintptrType, floatSize, false)
|
||||||
|
// Real parts
|
||||||
|
xReal := b.CreateLoad(floatType, xPtr, "x.real")
|
||||||
|
yReal := b.CreateLoad(floatType, yPtr, "y.real")
|
||||||
|
realEq := b.CreateFCmp(llvm.FloatOEQ, xReal, yReal, "eq.real")
|
||||||
|
// Imaginary parts
|
||||||
|
xImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), xPtr, []llvm.Value{imagOffset}, "")
|
||||||
|
yImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), yPtr, []llvm.Value{imagOffset}, "")
|
||||||
|
xImag := b.CreateLoad(floatType, xImagPtr, "x.imag")
|
||||||
|
yImag := b.CreateLoad(floatType, yImagPtr, "y.imag")
|
||||||
|
imagEq := b.CreateFCmp(llvm.FloatOEQ, xImag, yImag, "eq.imag")
|
||||||
|
return b.CreateAnd(realEq, imagEq, "")
|
||||||
|
}
|
||||||
|
// Integer/boolean: compare raw bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
case *types.Pointer, *types.Chan:
|
||||||
|
// Pointers and channels: compare as raw pointer-sized bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
case *types.Interface:
|
||||||
|
// Interface: use runtime interface equality.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hashmapInterfaceEqual", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
case *types.Struct:
|
||||||
|
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false) // start with true
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < keyType.NumFields(); i++ {
|
||||||
|
if keyType.Field(i).Name() == "_" {
|
||||||
|
continue // blank fields are ignored in Go equality
|
||||||
|
}
|
||||||
|
fieldType := keyType.Field(i).Type()
|
||||||
|
llvmFieldType := b.getLLVMType(fieldType)
|
||||||
|
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||||
|
continue // skip zero-sized fields
|
||||||
|
}
|
||||||
|
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||||
|
xFieldPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
yFieldPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
fieldEq := b.generateKeyEqual(fieldType, llvmFieldType, xFieldPtr, yFieldPtr, fn)
|
||||||
|
result = b.CreateAnd(result, fieldEq, "")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
case *types.Array:
|
||||||
|
elemType := keyType.Elem()
|
||||||
|
llvmElemType := b.getLLVMType(elemType)
|
||||||
|
arrayLen := keyType.Len()
|
||||||
|
if hashmapIsBinaryKey(elemType) {
|
||||||
|
// All elements are binary-comparable; compare the entire array.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
}
|
||||||
|
if arrayLen == 0 {
|
||||||
|
return llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||||
|
}
|
||||||
|
if arrayLen <= hashArrayUnrollLimit {
|
||||||
|
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < int(arrayLen); i++ {
|
||||||
|
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||||
|
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||||
|
result = b.CreateAnd(result, elemEq, "")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
|
||||||
|
loopEntry := b.GetInsertBlock()
|
||||||
|
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.body")
|
||||||
|
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.done")
|
||||||
|
|
||||||
|
b.CreateBr(loopBody)
|
||||||
|
b.SetInsertPointAtEnd(loopBody)
|
||||||
|
|
||||||
|
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||||
|
|
||||||
|
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, phiI}, "")
|
||||||
|
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, phiI}, "")
|
||||||
|
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||||
|
|
||||||
|
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||||
|
atEnd := b.CreateICmp(llvm.IntUGE, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||||
|
exitLoop := b.CreateOr(atEnd, b.CreateNot(elemEq, ""), "")
|
||||||
|
b.CreateCondBr(exitLoop, loopDone, loopBody)
|
||||||
|
|
||||||
|
bodyEnd := b.GetInsertBlock()
|
||||||
|
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||||
|
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||||
|
|
||||||
|
b.SetInsertPointAtEnd(loopDone)
|
||||||
|
return elemEq
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled key type for equal generation: %T", keyType))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-1
@@ -34,6 +34,7 @@ type functionInfo struct {
|
|||||||
interrupt bool // go:interrupt
|
interrupt bool // go:interrupt
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
noescape bool // go:noescape
|
noescape bool // go:noescape
|
||||||
|
noheap bool // go:noheap
|
||||||
variadic bool // go:variadic (CGo only)
|
variadic bool // go:variadic (CGo only)
|
||||||
inline inlineType // go:inline
|
inline inlineType // go:inline
|
||||||
}
|
}
|
||||||
@@ -161,7 +162,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
|
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
case "runtime.alloc":
|
case "runtime.alloc", "runtime.alloc_noheap":
|
||||||
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
||||||
// returns values that are never null and never alias to an existing value.
|
// returns values that are never null and never alias to an existing value.
|
||||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||||
@@ -190,6 +191,31 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
case "runtime.stringFromRunes":
|
case "runtime.stringFromRunes":
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
|
case "runtime.hashmapSet":
|
||||||
|
// The key (param 2) and value (param 3) pointers are only read via
|
||||||
|
// memcpy/hash/equal and are never captured. The indirect calls
|
||||||
|
// through m.keyHash and m.keyEqual function pointers prevent LLVM's
|
||||||
|
// functionattrs pass from inferring this automatically.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
case "runtime.hashmapGet":
|
||||||
|
// The key (param 2) is read-only and never captured.
|
||||||
|
// The value (param 3) is written to (receives the result) but never captured.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
case "runtime.hashmapDelete":
|
||||||
|
// The key (param 2) is read-only and never captured.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
case "runtime.hashmapGenericSet":
|
||||||
|
// Same as hashmapBinarySet: key (param 2) and value (param 3) are
|
||||||
|
// not captured.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
case "runtime.hashmapGenericGet":
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
|
case "runtime.hashmapGenericDelete":
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||||
case "runtime.trackPointer":
|
case "runtime.trackPointer":
|
||||||
// This function is necessary for tracking pointers on the stack in a
|
// This function is necessary for tracking pointers on the stack in a
|
||||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||||
@@ -451,6 +477,9 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
if len(f.Blocks) == 0 {
|
if len(f.Blocks) == 0 {
|
||||||
info.noescape = true
|
info.noescape = true
|
||||||
}
|
}
|
||||||
|
case "//go:noheap":
|
||||||
|
// Ensure this function does not allocate on the heap.
|
||||||
|
info.noheap = true
|
||||||
case "//go:variadic":
|
case "//go:variadic":
|
||||||
// The //go:variadic pragma is emitted by the CGo preprocessing
|
// The //go:variadic pragma is emitted by the CGo preprocessing
|
||||||
// pass for C variadic functions. This includes both explicit
|
// pass for C variadic functions. This includes both explicit
|
||||||
|
|||||||
Vendored
+29
-33
@@ -10,33 +10,30 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = add i32 %x, %y
|
%0 = add i32 %x, %y
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %x, %y
|
%0 = icmp eq i32 %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -50,14 +47,14 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.divideByZeroPanic(ptr) #1
|
declare void @runtime.divideByZeroPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -67,12 +64,12 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -86,12 +83,12 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -101,66 +98,66 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oeq float %x, %y
|
%0 = fcmp oeq float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp une float %x, %y
|
%0 = fcmp une float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp olt float %x, %y
|
%0 = fcmp olt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ole float %x, %y
|
%0 = fcmp ole float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ogt float %x, %y
|
%0 = fcmp ogt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oge float %x, %y
|
%0 = fcmp oge float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
|
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.r
|
ret float %x.r
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
|
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.i
|
ret float %x.i
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fadd float %x.r, %y.r
|
%0 = fadd float %x.r, %y.r
|
||||||
%1 = fadd float %x.i, %y.i
|
%1 = fadd float %x.i, %y.i
|
||||||
@@ -170,7 +167,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fsub float %x.r, %y.r
|
%0 = fsub float %x.r, %y.r
|
||||||
%1 = fsub float %x.i, %y.i
|
%1 = fsub float %x.i, %y.i
|
||||||
@@ -180,7 +177,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fmul float %x.r, %y.r
|
%0 = fmul float %x.r, %y.r
|
||||||
%1 = fmul float %x.i, %y.i
|
%1 = fmul float %x.i, %y.i
|
||||||
@@ -194,19 +191,18 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
|
define hidden void @main.foo(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
|
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+21
-25
@@ -6,76 +6,73 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
||||||
%runtime.chanSelectState = type { ptr, ptr }
|
%runtime.chanSelectState = type { ptr, ptr }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanIntSend(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(i64 4, 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(i64 16, ptr nonnull %chan.op)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
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 16, ptr nonnull %chan.op)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.end.p0(i64 4, 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) #3
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
|
||||||
|
|
||||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
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) #3
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
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(i64 4, ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
%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 4, ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
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(i64 16, ptr nonnull %chan.op)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
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(i64 16, ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
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(i64 16, ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
%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(i64 16, ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 {
|
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
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
|
||||||
@@ -88,7 +85,7 @@ entry:
|
|||||||
store ptr %ch2, ptr %0, align 4
|
store ptr %ch2, ptr %0, align 4
|
||||||
%.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) #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(i64 16, 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
|
||||||
@@ -105,10 +102,9 @@ select.body: ; preds = %select.next
|
|||||||
br label %select.done
|
br label %select.done
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1
|
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #0
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
attributes #3 = { nounwind }
|
||||||
attributes #4 = { nounwind }
|
|
||||||
|
|||||||
+23
-23
@@ -6,19 +6,16 @@ target triple = "thumbv7m-unknown-unknown-eabi"
|
|||||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
|
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
|
||||||
%runtime._interface = type { ptr, ptr }
|
%runtime._interface = type { ptr, ptr }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.external(ptr) #2
|
declare void @main.external(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferSimple(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%defer.alloca = alloca { i32, ptr }, align 4
|
%defer.alloca = alloca { i32, ptr }, align 4
|
||||||
%deferPtr = alloca ptr, align 4
|
%deferPtr = alloca ptr, align 4
|
||||||
@@ -112,14 +109,14 @@ rundefers.end3: ; preds = %rundefers.loophead6
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
||||||
declare ptr @llvm.stacksave.p0() #3
|
declare ptr @llvm.stacksave.p0() #2
|
||||||
|
|
||||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2
|
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #1
|
||||||
|
|
||||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
|
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printlock(ptr undef) #4
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||||
@@ -127,14 +124,14 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printlock(ptr) #2
|
declare void @runtime.printlock(ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #2
|
declare void @runtime.printint32(i32, ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printunlock(ptr) #2
|
declare void @runtime.printunlock(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%defer.alloca2 = alloca { i32, ptr }, align 4
|
%defer.alloca2 = alloca { i32, ptr }, align 4
|
||||||
%defer.alloca = alloca { i32, ptr }, align 4
|
%defer.alloca = alloca { i32, ptr }, align 4
|
||||||
@@ -253,7 +250,7 @@ rundefers.end7: ; preds = %rundefers.loophead1
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printlock(ptr undef) #4
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||||
@@ -262,7 +259,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printlock(ptr undef) #4
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 5, ptr undef) #4
|
call void @runtime.printint32(i32 5, ptr undef) #4
|
||||||
@@ -271,7 +268,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%deferPtr = alloca ptr, align 4
|
%deferPtr = alloca ptr, align 4
|
||||||
store ptr null, ptr %deferPtr, align 4
|
store ptr null, ptr %deferPtr, align 4
|
||||||
@@ -315,8 +312,11 @@ rundefers.end: ; preds = %rundefers.loophead
|
|||||||
br label %recover
|
br label %recover
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferLoop(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%deferPtr = alloca ptr, align 4
|
%deferPtr = alloca ptr, align 4
|
||||||
store ptr null, ptr %deferPtr, align 4
|
store ptr null, ptr %deferPtr, align 4
|
||||||
@@ -405,7 +405,7 @@ rundefers.end1: ; preds = %rundefers.loophead4
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%defer.alloca = alloca { i32, ptr, i32 }, align 4
|
%defer.alloca = alloca { i32, ptr, i32 }, align 4
|
||||||
%deferPtr = alloca ptr, align 4
|
%deferPtr = alloca ptr, align 4
|
||||||
@@ -505,9 +505,9 @@ rundefers.end4: ; preds = %rundefers.loophead7
|
|||||||
br label %recover
|
br label %recover
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #2 = { nocallback nofree nosync nounwind willreturn }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind willreturn }
|
attributes #3 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #4 = { nounwind }
|
attributes #4 = { nounwind }
|
||||||
attributes #5 = { nounwind returns_twice }
|
attributes #5 = { nounwind returns_twice }
|
||||||
|
|||||||
Vendored
+12
-16
@@ -3,19 +3,16 @@ source_filename = "float.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -27,25 +24,25 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.maxu32f(ptr %context) unnamed_addr #2 {
|
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float 0x41F0000000000000
|
ret float 0x41F0000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 -1
|
ret i32 -1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #2 {
|
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = uitofp i32 %v to float
|
%0 = uitofp i32 %v to float
|
||||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||||
@@ -55,7 +52,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #2 {
|
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -68,7 +65,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||||
@@ -80,7 +77,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||||
@@ -93,6 +90,5 @@ entry:
|
|||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
|
||||||
|
|||||||
Vendored
+11
-15
@@ -3,48 +3,44 @@ source_filename = "func.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #2 {
|
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq ptr %callback.funcptr, null
|
%0 = icmp eq ptr %callback.funcptr, null
|
||||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||||
|
|
||||||
fpcall.next: ; preds = %entry
|
fpcall.next: ; preds = %entry
|
||||||
call void %callback.funcptr(i32 3, ptr %callback.context) #3
|
call void %callback.funcptr(i32 3, ptr %callback.context) #2
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
fpcall.throw: ; preds = %entry
|
fpcall.throw: ; preds = %entry
|
||||||
call void @runtime.nilPanic(ptr undef) #3
|
call void @runtime.nilPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.nilPanic(ptr) #1
|
declare void @runtime.nilPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.bar(ptr %context) unnamed_addr #2 {
|
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
|
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #2 {
|
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+14
-14
@@ -25,19 +25,16 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
|
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
@@ -55,8 +52,11 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
|
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
@@ -72,7 +72,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
|
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
@@ -94,7 +94,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
|
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
|
||||||
@@ -103,7 +103,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
|
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
@@ -125,7 +125,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
|
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
|
||||||
@@ -139,7 +139,7 @@ entry:
|
|||||||
ret %runtime._interface %1
|
ret %runtime._interface %1
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+78
-160
@@ -1,144 +1,78 @@
|
|||||||
; ModuleID = 'generics.go'
|
; ModuleID = 'generics.go'
|
||||||
source_filename = "generics.go"
|
source_filename = "generics.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%"main.Point[int]" = type { i32, i32 }
|
|
||||||
%"main.Point[float32]" = type { float, float }
|
%"main.Point[float32]" = type { float, float }
|
||||||
|
%"main.Point[int]" = type { i32, i32 }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.main(i8* %context) unnamed_addr #1 {
|
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%bi = alloca %"main.Point[int]", align 8
|
%0 = call %"main.Point[float32]" @"main.Add[float32]"(float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, ptr undef)
|
||||||
%ai = alloca %"main.Point[int]", align 8
|
%1 = call %"main.Point[int]" @"main.Add[int]"(i32 0, i32 0, i32 0, i32 0, ptr undef)
|
||||||
%bf = alloca %"main.Point[float32]", align 8
|
|
||||||
%af = alloca %"main.Point[float32]", align 8
|
|
||||||
%af.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
|
||||||
store float 0.000000e+00, float* %af.repack, align 8
|
|
||||||
%af.repack1 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
|
||||||
store float 0.000000e+00, float* %af.repack1, align 4
|
|
||||||
%0 = bitcast %"main.Point[float32]"* %af to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
|
||||||
%bf.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
|
||||||
store float 0.000000e+00, float* %bf.repack, align 8
|
|
||||||
%bf.repack2 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
|
||||||
store float 0.000000e+00, float* %bf.repack2, align 4
|
|
||||||
%1 = bitcast %"main.Point[float32]"* %bf to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
|
||||||
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
|
||||||
%.unpack = load float, float* %.elt, align 8
|
|
||||||
%.elt3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
|
||||||
%.unpack4 = load float, float* %.elt3, align 4
|
|
||||||
%.elt5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
|
||||||
%.unpack6 = load float, float* %.elt5, align 8
|
|
||||||
%.elt7 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
|
||||||
%.unpack8 = load float, float* %.elt7, align 4
|
|
||||||
%2 = call %"main.Point[float32]" @"main.Add[float32]"(float %.unpack, float %.unpack4, float %.unpack6, float %.unpack8, i8* undef)
|
|
||||||
%ai.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
|
||||||
store i32 0, i32* %ai.repack, align 8
|
|
||||||
%ai.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
|
||||||
store i32 0, i32* %ai.repack9, align 4
|
|
||||||
%3 = bitcast %"main.Point[int]"* %ai to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %3, i8* undef) #2
|
|
||||||
%bi.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
|
||||||
store i32 0, i32* %bi.repack, align 8
|
|
||||||
%bi.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
|
||||||
store i32 0, i32* %bi.repack10, align 4
|
|
||||||
%4 = bitcast %"main.Point[int]"* %bi to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %4, i8* undef) #2
|
|
||||||
%.elt11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
|
||||||
%.unpack12 = load i32, i32* %.elt11, align 8
|
|
||||||
%.elt13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
|
||||||
%.unpack14 = load i32, i32* %.elt13, align 4
|
|
||||||
%.elt15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
|
||||||
%.unpack16 = load i32, i32* %.elt15, align 8
|
|
||||||
%.elt17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
|
||||||
%.unpack18 = load i32, i32* %.elt17, align 4
|
|
||||||
%5 = call %"main.Point[int]" @"main.Add[int]"(i32 %.unpack12, i32 %.unpack14, i32 %.unpack16, i32 %.unpack18, i8* undef)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, i8* %context) unnamed_addr #1 {
|
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca %"main.Point[float32]", align 8
|
%stackalloc = alloca i8, align 1
|
||||||
%b = alloca %"main.Point[float32]", align 8
|
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%a = alloca %"main.Point[float32]", align 8
|
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
%a.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
store float %a.X, ptr %a, align 4
|
||||||
store float 0.000000e+00, float* %a.repack, align 8
|
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%a.repack9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
store float %a.Y, ptr %a.repack9, align 4
|
||||||
store float 0.000000e+00, float* %a.repack9, align 4
|
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%0 = bitcast %"main.Point[float32]"* %a to i8*
|
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
store float %b.X, ptr %b, align 4
|
||||||
%a.repack10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
store float %a.X, float* %a.repack10, align 8
|
store float %b.Y, ptr %b.repack11, align 4
|
||||||
%a.repack11 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
call void @main.checkSize(i32 4, ptr undef) #3
|
||||||
store float %a.Y, float* %a.repack11, align 4
|
call void @main.checkSize(i32 8, ptr undef) #3
|
||||||
%b.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
store float 0.000000e+00, float* %b.repack, align 8
|
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
%b.repack13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
|
||||||
store float 0.000000e+00, float* %b.repack13, align 4
|
|
||||||
%1 = bitcast %"main.Point[float32]"* %b to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
|
||||||
%b.repack14 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
|
||||||
store float %b.X, float* %b.repack14, align 8
|
|
||||||
%b.repack15 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
|
||||||
store float %b.Y, float* %b.repack15, align 4
|
|
||||||
call void @main.checkSize(i32 4, i8* undef) #2
|
|
||||||
call void @main.checkSize(i32 8, i8* undef) #2
|
|
||||||
%complit.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
|
||||||
store float 0.000000e+00, float* %complit.repack, align 8
|
|
||||||
%complit.repack17 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
|
||||||
store float 0.000000e+00, float* %complit.repack17, align 4
|
|
||||||
%2 = bitcast %"main.Point[float32]"* %complit to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
|
||||||
%3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
|
||||||
br i1 false, label %deref.throw, label %deref.next
|
br i1 false, label %deref.throw, label %deref.next
|
||||||
|
|
||||||
deref.next: ; preds = %entry
|
deref.next: ; preds = %entry
|
||||||
br i1 false, label %deref.throw1, label %deref.next2
|
br i1 false, label %deref.throw1, label %deref.next2
|
||||||
|
|
||||||
deref.next2: ; preds = %deref.next
|
deref.next2: ; preds = %deref.next
|
||||||
%4 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
%0 = load float, ptr %a, align 4
|
||||||
%5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
%1 = load float, ptr %b, align 4
|
||||||
%6 = load float, float* %5, align 8
|
%2 = fadd float %0, %1
|
||||||
%7 = load float, float* %4, align 8
|
|
||||||
%8 = fadd float %6, %7
|
|
||||||
br i1 false, label %deref.throw3, label %deref.next4
|
br i1 false, label %deref.throw3, label %deref.next4
|
||||||
|
|
||||||
deref.next4: ; preds = %deref.next2
|
deref.next4: ; preds = %deref.next2
|
||||||
br i1 false, label %deref.throw5, label %deref.next6
|
br i1 false, label %deref.throw5, label %deref.next6
|
||||||
|
|
||||||
deref.next6: ; preds = %deref.next4
|
deref.next6: ; preds = %deref.next4
|
||||||
%9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
%10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%11 = load float, float* %10, align 4
|
%5 = load float, ptr %4, align 4
|
||||||
%12 = load float, float* %9, align 4
|
%6 = load float, ptr %3, align 4
|
||||||
br i1 false, label %store.throw, label %store.next
|
br i1 false, label %store.throw, label %store.next
|
||||||
|
|
||||||
store.next: ; preds = %deref.next6
|
store.next: ; preds = %deref.next6
|
||||||
store float %8, float* %3, align 8
|
store float %2, ptr %complit, align 4
|
||||||
br i1 false, label %store.throw7, label %store.next8
|
br i1 false, label %store.throw7, label %store.next8
|
||||||
|
|
||||||
store.next8: ; preds = %store.next
|
store.next8: ; preds = %store.next
|
||||||
%13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||||
%14 = fadd float %11, %12
|
%8 = fadd float %5, %6
|
||||||
store float %14, float* %13, align 4
|
store float %8, ptr %7, align 4
|
||||||
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
%.unpack = load float, ptr %complit, align 4
|
||||||
%.unpack = load float, float* %.elt, align 8
|
%9 = insertvalue %"main.Point[float32]" poison, float %.unpack, 0
|
||||||
%15 = insertvalue %"main.Point[float32]" undef, float %.unpack, 0
|
%10 = insertvalue %"main.Point[float32]" %9, float %8, 1
|
||||||
%16 = insertvalue %"main.Point[float32]" %15, float %14, 1
|
ret %"main.Point[float32]" %10
|
||||||
ret %"main.Point[float32]" %16
|
|
||||||
|
|
||||||
deref.throw: ; preds = %entry
|
deref.throw: ; preds = %entry
|
||||||
unreachable
|
unreachable
|
||||||
@@ -159,81 +93,64 @@ store.throw7: ; preds = %store.next
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.checkSize(i32, i8*) #0
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||||
|
|
||||||
declare void @runtime.nilPanic(i8*) #0
|
declare void @main.checkSize(i32, ptr) #0
|
||||||
|
|
||||||
|
declare void @runtime.nilPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, i8* %context) unnamed_addr #1 {
|
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca %"main.Point[int]", align 8
|
%stackalloc = alloca i8, align 1
|
||||||
%b = alloca %"main.Point[int]", align 8
|
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%a = alloca %"main.Point[int]", align 8
|
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
%a.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
store i32 %a.X, ptr %a, align 4
|
||||||
store i32 0, i32* %a.repack, align 8
|
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%a.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
store i32 %a.Y, ptr %a.repack9, align 4
|
||||||
store i32 0, i32* %a.repack9, align 4
|
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
%0 = bitcast %"main.Point[int]"* %a to i8*
|
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
store i32 %b.X, ptr %b, align 4
|
||||||
%a.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
store i32 %a.X, i32* %a.repack10, align 8
|
store i32 %b.Y, ptr %b.repack11, align 4
|
||||||
%a.repack11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
call void @main.checkSize(i32 4, ptr undef) #3
|
||||||
store i32 %a.Y, i32* %a.repack11, align 4
|
call void @main.checkSize(i32 8, ptr undef) #3
|
||||||
%b.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
store i32 0, i32* %b.repack, align 8
|
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
%b.repack13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
|
||||||
store i32 0, i32* %b.repack13, align 4
|
|
||||||
%1 = bitcast %"main.Point[int]"* %b to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
|
||||||
%b.repack14 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
|
||||||
store i32 %b.X, i32* %b.repack14, align 8
|
|
||||||
%b.repack15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
|
||||||
store i32 %b.Y, i32* %b.repack15, align 4
|
|
||||||
call void @main.checkSize(i32 4, i8* undef) #2
|
|
||||||
call void @main.checkSize(i32 8, i8* undef) #2
|
|
||||||
%complit.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
|
||||||
store i32 0, i32* %complit.repack, align 8
|
|
||||||
%complit.repack17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
|
||||||
store i32 0, i32* %complit.repack17, align 4
|
|
||||||
%2 = bitcast %"main.Point[int]"* %complit to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
|
||||||
%3 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
|
||||||
br i1 false, label %deref.throw, label %deref.next
|
br i1 false, label %deref.throw, label %deref.next
|
||||||
|
|
||||||
deref.next: ; preds = %entry
|
deref.next: ; preds = %entry
|
||||||
br i1 false, label %deref.throw1, label %deref.next2
|
br i1 false, label %deref.throw1, label %deref.next2
|
||||||
|
|
||||||
deref.next2: ; preds = %deref.next
|
deref.next2: ; preds = %deref.next
|
||||||
%4 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
%0 = load i32, ptr %a, align 4
|
||||||
%5 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
%1 = load i32, ptr %b, align 4
|
||||||
%6 = load i32, i32* %5, align 8
|
%2 = add i32 %0, %1
|
||||||
%7 = load i32, i32* %4, align 8
|
|
||||||
%8 = add i32 %6, %7
|
|
||||||
br i1 false, label %deref.throw3, label %deref.next4
|
br i1 false, label %deref.throw3, label %deref.next4
|
||||||
|
|
||||||
deref.next4: ; preds = %deref.next2
|
deref.next4: ; preds = %deref.next2
|
||||||
br i1 false, label %deref.throw5, label %deref.next6
|
br i1 false, label %deref.throw5, label %deref.next6
|
||||||
|
|
||||||
deref.next6: ; preds = %deref.next4
|
deref.next6: ; preds = %deref.next4
|
||||||
%9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
%10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%11 = load i32, i32* %10, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%12 = load i32, i32* %9, align 4
|
%6 = load i32, ptr %3, align 4
|
||||||
br i1 false, label %store.throw, label %store.next
|
br i1 false, label %store.throw, label %store.next
|
||||||
|
|
||||||
store.next: ; preds = %deref.next6
|
store.next: ; preds = %deref.next6
|
||||||
store i32 %8, i32* %3, align 8
|
store i32 %2, ptr %complit, align 4
|
||||||
br i1 false, label %store.throw7, label %store.next8
|
br i1 false, label %store.throw7, label %store.next8
|
||||||
|
|
||||||
store.next8: ; preds = %store.next
|
store.next8: ; preds = %store.next
|
||||||
%13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||||
%14 = add i32 %11, %12
|
%8 = add i32 %5, %6
|
||||||
store i32 %14, i32* %13, align 4
|
store i32 %8, ptr %7, align 4
|
||||||
%.elt = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
%.unpack = load i32, ptr %complit, align 4
|
||||||
%.unpack = load i32, i32* %.elt, align 8
|
%9 = insertvalue %"main.Point[int]" poison, i32 %.unpack, 0
|
||||||
%15 = insertvalue %"main.Point[int]" undef, i32 %.unpack, 0
|
%10 = insertvalue %"main.Point[int]" %9, i32 %8, 1
|
||||||
%16 = insertvalue %"main.Point[int]" %15, i32 %14, 1
|
ret %"main.Point[int]" %10
|
||||||
ret %"main.Point[int]" %16
|
|
||||||
|
|
||||||
deref.throw: ; preds = %entry
|
deref.throw: ; preds = %entry
|
||||||
unreachable
|
unreachable
|
||||||
@@ -254,6 +171,7 @@ store.throw7: ; preds = %store.next
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind }
|
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+13
-17
@@ -5,27 +5,24 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
%runtime._string = type { ptr, i32 }
|
%runtime._string = type { ptr, i32 }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret ptr %s.data
|
ret ptr %s.data
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = icmp slt i16 %len, 0
|
%0 = icmp slt i16 %len, 0
|
||||||
@@ -39,25 +36,24 @@ unsafe.String.next: ; preds = %entry
|
|||||||
%5 = zext nneg i16 %len to i32
|
%5 = zext nneg i16 %len to i32
|
||||||
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
||||||
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
||||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret %runtime._string %7
|
ret %runtime._string %7
|
||||||
|
|
||||||
unsafe.String.throw: ; preds = %entry
|
unsafe.String.throw: ; preds = %entry
|
||||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret ptr %s.data
|
ret ptr %s.data
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+39
-43
@@ -5,35 +5,32 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
%runtime._string = type { ptr, i32 }
|
%runtime._string = type { ptr, i32 }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %a
|
ret i32 %a
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare i32 @llvm.smin.i32(i32, i32) #3
|
declare i32 @llvm.smin.i32(i32, i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||||
@@ -41,7 +38,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||||
@@ -50,109 +47,109 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare i8 @llvm.umin.i8(i8, i8) #3
|
declare i8 @llvm.umin.i8(i8, i8) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare i32 @llvm.umin.i32(i32, i32) #3
|
declare i32 @llvm.umin.i32(i32, i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call float @llvm.minimum.f32(float %a, float %b)
|
%0 = call float @llvm.minimum.f32(float %a, float %b)
|
||||||
ret float %0
|
ret float %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare float @llvm.minimum.f32(float, float) #3
|
declare float @llvm.minimum.f32(float, float) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call double @llvm.minimum.f64(double %a, double %b)
|
%0 = call double @llvm.minimum.f64(double %a, double %b)
|
||||||
ret double %0
|
ret double %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare double @llvm.minimum.f64(double, double) #3
|
declare double @llvm.minimum.f64(double, double) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
|
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #4
|
||||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||||
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
ret %runtime._string %5
|
ret %runtime._string %5
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare i32 @llvm.smax.i32(i32, i32) #3
|
declare i32 @llvm.smax.i32(i32, i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare i32 @llvm.umax.i32(i32, i32) #3
|
declare i32 @llvm.umax.i32(i32, i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call float @llvm.maximum.f32(float %a, float %b)
|
%0 = call float @llvm.maximum.f32(float %a, float %b)
|
||||||
ret float %0
|
ret float %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare float @llvm.maximum.f32(float, float) #3
|
declare float @llvm.maximum.f32(float, float) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
|
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #4
|
||||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||||
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
ret %runtime._string %5
|
ret %runtime._string %5
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = shl i32 %s.len, 2
|
%0 = shl i32 %s.len, 2
|
||||||
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
||||||
@@ -160,26 +157,25 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; 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 nocapture writeonly, i8, i32, i1 immarg) #4
|
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
|
define hidden void @main.clearMap(ptr dereferenceable_or_null(48) %m, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
|
call void @runtime.hashmapClear(ptr %m, ptr undef) #4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(48), ptr) #0
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
attributes #4 = { nounwind }
|
||||||
attributes #5 = { nounwind }
|
|
||||||
|
|||||||
+28
-28
@@ -5,39 +5,36 @@ target triple = "thumbv7m-unknown-unknown-eabi"
|
|||||||
|
|
||||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, ptr) #2
|
declare void @main.regularFunction(i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #2
|
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #1
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
|
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
||||||
@@ -45,13 +42,13 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||||
@@ -59,7 +56,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
||||||
store i32 3, ptr %n, align 4
|
store i32 3, ptr %n, align 4
|
||||||
@@ -76,8 +73,11 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
store i32 7, ptr %context, align 4
|
store i32 7, ptr %context, align 4
|
||||||
ret void
|
ret void
|
||||||
@@ -93,14 +93,14 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printlock(ptr) #2
|
declare void @runtime.printlock(ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #2
|
declare void @runtime.printint32(i32, ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printunlock(ptr) #2
|
declare void @runtime.printunlock(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
@@ -126,13 +126,13 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||||
@@ -146,16 +146,16 @@ declare i32 @llvm.umin.i32(i32, i32) #7
|
|||||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
||||||
store ptr %itf.value, ptr %0, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
@@ -186,11 +186,11 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #2 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
|
|||||||
+29
-29
@@ -5,30 +5,27 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, ptr) #1
|
declare void @main.regularFunction(i32, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.deadlock(ptr) #1
|
declare void @runtime.deadlock(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||||
@@ -36,23 +33,23 @@ entry:
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||||
@@ -61,7 +58,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
||||||
@@ -82,8 +79,11 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
store i32 7, ptr %context, align 4
|
store i32 7, ptr %context, align 4
|
||||||
ret void
|
ret void
|
||||||
@@ -100,14 +100,14 @@ entry:
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printlock(ptr) #1
|
declare void @runtime.printlock(ptr) #0
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #1
|
declare void @runtime.printint32(i32, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.printunlock(ptr) #1
|
declare void @runtime.printunlock(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
||||||
@@ -135,13 +135,13 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||||
@@ -155,16 +155,16 @@ declare i32 @llvm.umin.i32(i32, i32) #7
|
|||||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
||||||
@@ -197,11 +197,11 @@ entry:
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #4 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
||||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
|
|||||||
Vendored
+37
-41
@@ -19,57 +19,54 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
||||||
@"reflect/types.typeid:basic:int" = external constant i8
|
@"reflect/types.typeid:basic:int" = external constant i8
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
|
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
|
||||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
@@ -79,12 +76,12 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
|
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
@@ -94,12 +91,12 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
|
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
@@ -109,34 +106,33 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
|
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
|
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #6
|
||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
|
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
|
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #6
|
||||||
%1 = extractvalue %runtime._string %0, 0
|
%1 = extractvalue %runtime._string %0, 0
|
||||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._string %0
|
ret %runtime._string %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
|
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
||||||
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||||
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #6 = { nounwind }
|
||||||
attributes #7 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+12
-16
@@ -3,48 +3,44 @@ source_filename = "pointer.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #2 {
|
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret [0 x i32] zeroinitializer
|
ret [0 x i32] zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret ptr %x
|
ret ptr %x
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret ptr %x
|
ret ptr %x
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret ptr %x
|
ret ptr %x
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+5
@@ -115,3 +115,8 @@ func doesNotEscapeParam(a *int, b []int, c chan int, d *[0]byte)
|
|||||||
//go:noescape
|
//go:noescape
|
||||||
func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
|
func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:noheap
|
||||||
|
func doesHeapAlloc() *int {
|
||||||
|
return new(int)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+40
-30
@@ -11,95 +11,105 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@undefinedGlobalNotInSection = external global i32, align 4
|
@undefinedGlobalNotInSection = external global i32, align 4
|
||||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define void @extern_func() #3 {
|
define void @extern_func() #2 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #2 {
|
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @somepkg.someFunction2(ptr) #1
|
declare void @somepkg.someFunction2(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: inlinehint nounwind
|
; Function Attrs: inlinehint nounwind
|
||||||
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #4 {
|
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #5 {
|
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.useGeneric(ptr %context) unnamed_addr #2 {
|
define hidden void @main.useGeneric(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"main.noinlineGenericFunc[int8]"(ptr undef)
|
call void @"main.noinlineGenericFunc[int8]"(ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #5 {
|
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.functionInSection(ptr %context) unnamed_addr #5 section ".special_function_section" {
|
define hidden void @main.functionInSection(ptr %context) unnamed_addr #4 section ".special_function_section" {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define void @exportedFunctionInSection() #6 section ".special_function_section" {
|
define void @exportedFunctionInSection() #5 section ".special_function_section" {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.declaredImport() #7
|
declare void @main.declaredImport() #6
|
||||||
|
|
||||||
declare void @imported() #8
|
declare void @imported() #7
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define void @exported() #9 {
|
define void @exported() #8 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
declare void @main.undefinedFunctionNotInSection(ptr) #0
|
||||||
|
|
||||||
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #1
|
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #2 {
|
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
; Function Attrs: nounwind
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
define hidden ptr @main.doesHeapAlloc(ptr %context) unnamed_addr #1 {
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
entry:
|
||||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
%stackalloc = alloca i8, align 1
|
||||||
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
%new = call align 4 dereferenceable(4) ptr @runtime.alloc_noheap(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #10
|
||||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #10
|
||||||
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
|
ret ptr %new
|
||||||
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
}
|
||||||
attributes #8 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
|
||||||
attributes #9 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc_noheap(i32, ptr, ptr) #9
|
||||||
|
|
||||||
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
||||||
|
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
|
||||||
|
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||||
|
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||||
|
attributes #8 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
|
||||||
|
attributes #9 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #10 = { nounwind }
|
||||||
|
|||||||
Vendored
+31
-31
@@ -3,31 +3,28 @@ source_filename = "slice.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.len
|
ret i32 %ints.len
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.cap
|
ret i32 %ints.cap
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%.not = icmp ult i32 %index, %ints.len
|
%.not = icmp ult i32 %index, %ints.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
@@ -42,10 +39,10 @@ lookup.throw: ; preds = %entry
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.lookupPanic(ptr) #1
|
declare void @runtime.lookupPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
||||||
@@ -66,10 +63,13 @@ entry:
|
|||||||
ret { ptr, i32, i32 } %4
|
ret { ptr, i32, i32 } %4
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||||
|
|
||||||
|
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
||||||
@@ -84,7 +84,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||||
%copy.size = shl nuw i32 %copy.n, 2
|
%copy.size = shl nuw i32 %copy.n, 2
|
||||||
@@ -99,7 +99,7 @@ declare i32 @llvm.umin.i32(i32, i32) #3
|
|||||||
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #4
|
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp slt i32 %len, 0
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
@@ -118,10 +118,10 @@ slice.throw: ; preds = %entry
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.slicePanic(ptr) #1
|
declare void @runtime.slicePanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp slt i32 %len, 0
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
@@ -142,7 +142,7 @@ slice.throw: ; preds = %entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||||
@@ -163,7 +163,7 @@ slice.throw: ; preds = %entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||||
@@ -184,7 +184,7 @@ slice.throw: ; preds = %entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = getelementptr i8, ptr %p, i32 %len
|
%0 = getelementptr i8, ptr %p, i32 %len
|
||||||
@@ -193,7 +193,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = trunc i64 %len to i32
|
%0 = trunc i64 %len to i32
|
||||||
@@ -203,7 +203,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp ult i32 %s.len, 4
|
%0 = icmp ult i32 %s.len, 4
|
||||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
@@ -216,10 +216,10 @@ slicetoarray.throw: ; preds = %entry
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.sliceToArrayPointerPanic(ptr) #1
|
declare void @runtime.sliceToArrayPointerPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5
|
||||||
@@ -234,7 +234,7 @@ slicetoarray.throw: ; preds = %entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = icmp ugt i32 %len, 1073741823
|
%0 = icmp ugt i32 %len, 1073741823
|
||||||
@@ -256,10 +256,10 @@ unsafe.Slice.throw: ; preds = %entry
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = icmp eq ptr %ptr, null
|
%0 = icmp eq ptr %ptr, null
|
||||||
@@ -281,7 +281,7 @@ unsafe.Slice.throw: ; preds = %entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = icmp ugt i64 %len, 1073741823
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
@@ -305,7 +305,7 @@ unsafe.Slice.throw: ; preds = %entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
|
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = icmp ugt i64 %len, 1073741823
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
@@ -328,9 +328,9 @@ unsafe.Slice.throw: ; preds = %entry
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #5 = { nounwind }
|
attributes #5 = { nounwind }
|
||||||
|
|||||||
Vendored
+21
-25
@@ -7,37 +7,34 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
|
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._string { ptr @"main$string", i32 3 }
|
ret %runtime._string { ptr @"main$string", i32 3 }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._string zeroinitializer
|
ret %runtime._string zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %s.len
|
ret i32 %s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%.not = icmp ult i32 %index, %s.len
|
%.not = icmp ult i32 %index, %s.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
@@ -48,40 +45,40 @@ lookup.next: ; preds = %entry
|
|||||||
ret i8 %1
|
ret i8 %1
|
||||||
|
|
||||||
lookup.throw: ; preds = %entry
|
lookup.throw: ; preds = %entry
|
||||||
call void @runtime.lookupPanic(ptr undef) #3
|
call void @runtime.lookupPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.lookupPanic(ptr) #1
|
declare void @runtime.lookupPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2
|
||||||
%1 = xor i1 %0, true
|
%1 = xor i1 %0, true
|
||||||
ret i1 %1
|
ret i1 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #2
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
|
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = zext i8 %x to i32
|
%0 = zext i8 %x to i32
|
||||||
%.not = icmp ugt i32 %s.len, %0
|
%.not = icmp ugt i32 %s.len, %0
|
||||||
@@ -93,11 +90,10 @@ lookup.next: ; preds = %entry
|
|||||||
ret i8 %2
|
ret i8 %2
|
||||||
|
|
||||||
lookup.throw: ; preds = %entry
|
lookup.throw: ; preds = %entry
|
||||||
call void @runtime.lookupPanic(ptr undef) #3
|
call void @runtime.lookupPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+24
-54
@@ -5,19 +5,16 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
%main.hasPadding = type { i1, i32, i1 }
|
%main.hasPadding = type { i1, i32, i1 }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
|
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%hashmap.key = alloca %main.hasPadding, align 8
|
%hashmap.key = alloca %main.hasPadding, align 8
|
||||||
%hashmap.value = alloca i32, align 4
|
%hashmap.value = alloca i32, align 4
|
||||||
@@ -27,29 +24,23 @@ entry:
|
|||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
%3 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
|
||||||
%4 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #5
|
|
||||||
%5 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #5
|
|
||||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
||||||
%6 = 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(i64 4, ptr nonnull %hashmap.value)
|
||||||
ret i32 %6
|
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) #4
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
||||||
|
|
||||||
declare void @runtime.memzero(ptr, i32, ptr) #1
|
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0
|
||||||
|
|
||||||
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
|
|
||||||
|
|
||||||
; 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) #4
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
|
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 {
|
||||||
entry:
|
entry:
|
||||||
%hashmap.key = alloca %main.hasPadding, align 8
|
%hashmap.key = alloca %main.hasPadding, align 8
|
||||||
%hashmap.value = alloca i32, align 4
|
%hashmap.value = alloca i32, align 4
|
||||||
@@ -60,20 +51,16 @@ entry:
|
|||||||
store i32 5, ptr %hashmap.value, align 4
|
store i32 5, ptr %hashmap.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||||
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
store %main.hasPadding %2, ptr %hashmap.key, align 4
|
||||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
|
||||||
%4 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #5
|
|
||||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #5
|
|
||||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #1
|
declare void @runtime.hashmapGenericSet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
|
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #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
|
||||||
@@ -84,23 +71,15 @@ entry:
|
|||||||
%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 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
%0 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
||||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #5
|
|
||||||
%2 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 13
|
|
||||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #5
|
|
||||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 21
|
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
|
||||||
%4 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #5
|
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
||||||
%5 = 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(i64 4, ptr nonnull %hashmap.value)
|
||||||
ret i32 %5
|
ret i32 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
|
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #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
|
||||||
@@ -112,29 +91,20 @@ entry:
|
|||||||
%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 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
|
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
||||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
|
||||||
%1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #5
|
|
||||||
%2 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 13
|
|
||||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #5
|
|
||||||
%3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 21
|
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
|
|
||||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #5
|
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.main(ptr %context) unnamed_addr #2 {
|
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
attributes #4 = { nounwind }
|
||||||
attributes #5 = { nounwind }
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ func TestErrors(t *testing.T) {
|
|||||||
{name: "loader-invaliddep"},
|
{name: "loader-invaliddep"},
|
||||||
{name: "loader-invalidpackage"},
|
{name: "loader-invalidpackage"},
|
||||||
{name: "loader-nopackage"},
|
{name: "loader-nopackage"},
|
||||||
|
{name: "noheap", target: "cortex-m-qemu"},
|
||||||
{name: "optimizer"},
|
{name: "optimizer"},
|
||||||
{name: "syntax"},
|
{name: "syntax"},
|
||||||
{name: "types"},
|
{name: "types"},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ require (
|
|||||||
golang.org/x/tools v0.30.0
|
golang.org/x/tools v0.30.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
tinygo.org/x/espflasher v0.6.0
|
tinygo.org/x/espflasher v0.6.0
|
||||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74
|
tinygo.org/x/go-llvm v0.0.0-20260422095634-06c6725fe5e6
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@@ -120,5 +120,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
tinygo.org/x/espflasher v0.6.0 h1:CHbGMHAIWq1tB8FKd/QwBpNFw1jvHHxpmvZiF+QOYUo=
|
tinygo.org/x/espflasher v0.6.0 h1:CHbGMHAIWq1tB8FKd/QwBpNFw1jvHHxpmvZiF+QOYUo=
|
||||||
tinygo.org/x/espflasher v0.6.0/go.mod h1:tr5u08HoE67WD5zxJesCiiVF/R1b6Akz3yXwh5zah8U=
|
tinygo.org/x/espflasher v0.6.0/go.mod h1:tr5u08HoE67WD5zxJesCiiVF/R1b6Akz3yXwh5zah8U=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 h1:ovavgTdIBWCH8YWlcfq9gkpoyT1+IxMKSn+Df27QwE8=
|
tinygo.org/x/go-llvm v0.0.0-20260422095634-06c6725fe5e6 h1:QSnqFgNV2Ij0T4hM2qKv53fcDAFElxClPjVUZXzYkWU=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
tinygo.org/x/go-llvm v0.0.0-20260422095634-06c6725fe5e6/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const version = "0.41.1"
|
const version = "0.42.0-dev"
|
||||||
|
|
||||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||||
// (like 0.30.0-dev-abcd012).
|
// (like 0.30.0-dev-abcd012).
|
||||||
|
|||||||
+2
-1
@@ -19,6 +19,7 @@ var (
|
|||||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||||
|
errLoopTooLong = errors.New("interp: loop ran too many iterations")
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is one of the errors that can be returned from toLLVMValue when the
|
// This is one of the errors that can be returned from toLLVMValue when the
|
||||||
@@ -29,7 +30,7 @@ var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not
|
|||||||
func isRecoverableError(err error) bool {
|
func isRecoverableError(err error) bool {
|
||||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||||
err == errLoopUnrolled || err == errInvalidPtrToIntSize
|
err == errLoopUnrolled || err == errLoopTooLong || err == errInvalidPtrToIntSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorLine is one line in a traceback. The position may be missing.
|
// ErrorLine is one line in a traceback. The position may be missing.
|
||||||
|
|||||||
+33
-30
@@ -19,35 +19,38 @@ const checks = true
|
|||||||
|
|
||||||
// runner contains all state related to one interp run.
|
// runner contains all state related to one interp run.
|
||||||
type runner struct {
|
type runner struct {
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
builder llvm.Builder
|
builder llvm.Builder
|
||||||
pointerSize uint32 // cached pointer size from the TargetData
|
pointerSize uint32 // cached pointer size from the TargetData
|
||||||
dataPtrType llvm.Type // often used type so created in advance
|
dataPtrType llvm.Type // often used type so created in advance
|
||||||
uintptrType llvm.Type // equivalent to uintptr in Go
|
uintptrType llvm.Type // equivalent to uintptr in Go
|
||||||
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
|
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
|
||||||
byteOrder binary.ByteOrder // big-endian or little-endian
|
byteOrder binary.ByteOrder // big-endian or little-endian
|
||||||
debug bool // log debug messages
|
debug bool // log debug messages
|
||||||
pkgName string // package name of the currently executing package
|
pkgName string // package name of the currently executing package
|
||||||
functionCache map[llvm.Value]*function // cache of compiled functions
|
functionCache map[llvm.Value]*function // cache of compiled functions
|
||||||
objects []object // slice of objects in memory
|
objects []object // slice of objects in memory
|
||||||
globals map[llvm.Value]int // map from global to index in objects slice
|
globals map[llvm.Value]int // map from global to index in objects slice
|
||||||
start time.Time
|
start time.Time
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
callsExecuted uint64
|
maxLoopIterations int
|
||||||
|
callsExecuted uint64
|
||||||
|
interpErr error // set by Uint/Int when they encounter pointer data
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
func newRunner(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bool) *runner {
|
||||||
r := runner{
|
r := runner{
|
||||||
mod: mod,
|
mod: mod,
|
||||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||||
byteOrder: llvmutil.ByteOrder(mod.Target()),
|
byteOrder: llvmutil.ByteOrder(mod.Target()),
|
||||||
debug: debug,
|
debug: debug,
|
||||||
functionCache: make(map[llvm.Value]*function),
|
functionCache: make(map[llvm.Value]*function),
|
||||||
objects: []object{{}},
|
objects: []object{{}},
|
||||||
globals: make(map[llvm.Value]int),
|
globals: make(map[llvm.Value]int),
|
||||||
start: time.Now(),
|
start: time.Now(),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
|
maxLoopIterations: maxLoopIterations,
|
||||||
}
|
}
|
||||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||||
r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
@@ -64,8 +67,8 @@ func (r *runner) dispose() {
|
|||||||
|
|
||||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
func Run(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bool) error {
|
||||||
r := newRunner(mod, timeout, debug)
|
r := newRunner(mod, timeout, maxLoopIterations, debug)
|
||||||
defer r.dispose()
|
defer r.dispose()
|
||||||
|
|
||||||
initAll := mod.NamedFunction("runtime.initAll")
|
initAll := mod.NamedFunction("runtime.initAll")
|
||||||
@@ -204,10 +207,10 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
|||||||
|
|
||||||
// RunFunc evaluates a single package initializer at compile time.
|
// RunFunc evaluates a single package initializer at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
|
func RunFunc(fn llvm.Value, timeout time.Duration, maxLoopIterations int, debug bool) error {
|
||||||
// Create and initialize *runner object.
|
// Create and initialize *runner object.
|
||||||
mod := fn.GlobalParent()
|
mod := fn.GlobalParent()
|
||||||
r := newRunner(mod, timeout, debug)
|
r := newRunner(mod, timeout, maxLoopIterations, debug)
|
||||||
defer r.dispose()
|
defer r.dispose()
|
||||||
initName := fn.Name()
|
initName := fn.Name()
|
||||||
if !strings.HasSuffix(initName, ".init") {
|
if !strings.HasSuffix(initName, ".init") {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
defer mod.Dispose()
|
defer mod.Dispose()
|
||||||
|
|
||||||
// Perform the transform.
|
// Perform the transform.
|
||||||
err = Run(mod, 10*time.Minute, false)
|
err = Run(mod, 10*time.Minute, DefaultMaxInterpBlockEntries, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, match := err.(*Error); match {
|
if err, match := err.(*Error); match {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
|
|||||||
+31
-1
@@ -12,6 +12,12 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultMaxInterpBlockEntries is the default maximum number of times a single
|
||||||
|
// basic block may be entered during interpretation of one function call. This
|
||||||
|
// limits how far the interpreter will unroll or evaluate loops before deferring
|
||||||
|
// the init function to runtime.
|
||||||
|
const DefaultMaxInterpBlockEntries = 1000
|
||||||
|
|
||||||
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
|
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
|
||||||
mem := memoryView{r: r, parent: parentMem}
|
mem := memoryView{r: r, parent: parentMem}
|
||||||
locals := make([]value, len(fn.locals))
|
locals := make([]value, len(fn.locals))
|
||||||
@@ -26,6 +32,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// This is used to prevent unrolling.
|
// This is used to prevent unrolling.
|
||||||
var runtimeBlocks map[int]struct{}
|
var runtimeBlocks map[int]struct{}
|
||||||
|
|
||||||
|
// Track how many times each basic block has been entered, to detect
|
||||||
|
// loops that are too expensive to evaluate at compile time.
|
||||||
|
var blockCounts map[int]int
|
||||||
|
|
||||||
// Start with the first basic block and the first instruction.
|
// Start with the first basic block and the first instruction.
|
||||||
// Branch instructions may modify both bb and instIndex when branching.
|
// Branch instructions may modify both bb and instIndex when branching.
|
||||||
bb := fn.blocks[0]
|
bb := fn.blocks[0]
|
||||||
@@ -36,6 +46,18 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||||
if instIndex == 0 {
|
if instIndex == 0 {
|
||||||
// This is the start of a new basic block.
|
// This is the start of a new basic block.
|
||||||
|
|
||||||
|
// Check whether this block has been entered too many times,
|
||||||
|
// which indicates an expensive loop that should be deferred
|
||||||
|
// to runtime.
|
||||||
|
if blockCounts == nil {
|
||||||
|
blockCounts = make(map[int]int)
|
||||||
|
}
|
||||||
|
blockCounts[currentBB]++
|
||||||
|
if r.maxLoopIterations > 0 && blockCounts[currentBB] > r.maxLoopIterations {
|
||||||
|
return nil, mem, r.errorAt(bb.instructions[0], errLoopTooLong)
|
||||||
|
}
|
||||||
|
|
||||||
if len(mem.instructions) != startRTInsts {
|
if len(mem.instructions) != startRTInsts {
|
||||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||||
// This loop has been unrolled.
|
// This loop has been unrolled.
|
||||||
@@ -277,7 +299,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// means that monotonic time in the time package is counted from
|
// means that monotonic time in the time package is counted from
|
||||||
// time.Time{}.Sub(1), which should be fine.
|
// time.Time{}.Sub(1), which should be fine.
|
||||||
locals[inst.localIndex] = literalValue{uint64(0)}
|
locals[inst.localIndex] = literalValue{uint64(0)}
|
||||||
case callFn.name == "runtime.alloc":
|
case callFn.name == "runtime.alloc" || callFn.name == "runtime.alloc_noheap":
|
||||||
// Allocate heap memory. At compile time, this is instead done
|
// Allocate heap memory. At compile time, this is instead done
|
||||||
// by creating a global variable.
|
// by creating a global variable.
|
||||||
|
|
||||||
@@ -825,6 +847,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
return nil, mem, r.errorAt(inst, errUnsupportedInst)
|
return nil, mem, r.errorAt(inst, errUnsupportedInst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if an instruction triggered a recoverable error (e.g.,
|
||||||
|
// trying to interpret pointer data as integer bytes).
|
||||||
|
if r.interpErr != nil {
|
||||||
|
err := r.interpErr
|
||||||
|
r.interpErr = nil
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
|
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-5
@@ -556,11 +556,13 @@ func (v pointerValue) asRawValue(r *runner) rawValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v pointerValue) Uint(r *runner) uint64 {
|
func (v pointerValue) Uint(r *runner) uint64 {
|
||||||
panic("cannot convert pointer to integer")
|
r.interpErr = errUnsupportedInst
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v pointerValue) Int(r *runner) int64 {
|
func (v pointerValue) Int(r *runner) int64 {
|
||||||
panic("cannot convert pointer to integer")
|
r.interpErr = errUnsupportedInst
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v pointerValue) equal(rhs pointerValue) bool {
|
func (v pointerValue) equal(rhs pointerValue) bool {
|
||||||
@@ -736,11 +738,12 @@ func (v rawValue) asRawValue(r *runner) rawValue {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v rawValue) bytes() []byte {
|
func (v rawValue) bytes(r *runner) []byte {
|
||||||
buf := make([]byte, len(v.buf))
|
buf := make([]byte, len(v.buf))
|
||||||
for i, p := range v.buf {
|
for i, p := range v.buf {
|
||||||
if p > 255 {
|
if p > 255 {
|
||||||
panic("cannot convert pointer value to byte")
|
r.interpErr = errUnsupportedInst
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
buf[i] = byte(p)
|
buf[i] = byte(p)
|
||||||
}
|
}
|
||||||
@@ -748,7 +751,10 @@ func (v rawValue) bytes() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v rawValue) Uint(r *runner) uint64 {
|
func (v rawValue) Uint(r *runner) uint64 {
|
||||||
buf := v.bytes()
|
buf := v.bytes(r)
|
||||||
|
if r.interpErr != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
switch len(v.buf) {
|
switch len(v.buf) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|||||||
+1
-1
@@ -245,7 +245,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
|||||||
"internal/cm/": false,
|
"internal/cm/": false,
|
||||||
"internal/futex/": false,
|
"internal/futex/": false,
|
||||||
"internal/fuzz/": false,
|
"internal/fuzz/": false,
|
||||||
"internal/itoa": false,
|
"internal/itoa/": false,
|
||||||
"internal/reflectlite/": false,
|
"internal/reflectlite/": false,
|
||||||
"internal/gclayout": false,
|
"internal/gclayout": false,
|
||||||
"internal/task/": false,
|
"internal/task/": false,
|
||||||
|
|||||||
@@ -304,6 +304,29 @@ func (p *Program) Sorted() []*Package {
|
|||||||
return p.sorted
|
return p.sorted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StripVarInitializer removes the package-level initializer for the named
|
||||||
|
// variable from the type info. This prevents go/ssa from emitting an init
|
||||||
|
// store for it, leaving the global zero-initialized in the IR. An external
|
||||||
|
// value (e.g. from -ldflags -X via makeGlobalsModule) can then be linked in
|
||||||
|
// at final link time without any runtime init overwriting it.
|
||||||
|
//
|
||||||
|
// Must be called after Parse() (typechecking populates InitOrder) and before
|
||||||
|
// LoadSSA() (which consumes InitOrder to build the package init function).
|
||||||
|
//
|
||||||
|
// Only 1:1 var initializers (var x = expr) are matched. Multi-variable
|
||||||
|
// initializers (var x, y = f()) are left untouched.
|
||||||
|
func (p *Package) StripVarInitializer(name string) {
|
||||||
|
n := 0
|
||||||
|
for _, init := range p.info.InitOrder {
|
||||||
|
if len(init.Lhs) == 1 && init.Lhs[0].Name() == name {
|
||||||
|
continue // drop this initializer
|
||||||
|
}
|
||||||
|
p.info.InitOrder[n] = init
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
p.info.InitOrder = p.info.InitOrder[:n]
|
||||||
|
}
|
||||||
|
|
||||||
// MainPkg returns the last package in the Sorted() slice. This is the main
|
// MainPkg returns the last package in the Sorted() slice. This is the main
|
||||||
// package of the program.
|
// package of the program.
|
||||||
func (p *Program) MainPkg() *Package {
|
func (p *Program) MainPkg() *Package {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/diagnostics"
|
"github.com/tinygo-org/tinygo/diagnostics"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
"github.com/tinygo-org/tinygo/interp"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/buildutil"
|
"golang.org/x/tools/go/buildutil"
|
||||||
"tinygo.org/x/espflasher/pkg/espflasher"
|
"tinygo.org/x/espflasher/pkg/espflasher"
|
||||||
@@ -588,7 +589,7 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
|
|||||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||||
}
|
}
|
||||||
if options.Monitor {
|
if options.Monitor {
|
||||||
return Monitor(result.Executable, "", config)
|
return Monitor(result.Executable, port, config)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1738,6 +1739,7 @@ func main() {
|
|||||||
serial := flag.String("serial", "", "which serial output to use (none, uart, usb, rtt)")
|
serial := flag.String("serial", "", "which serial output to use (none, uart, usb, rtt)")
|
||||||
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
||||||
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
||||||
|
interpLoopLimit := flag.Int("interp-loop-limit", interp.DefaultMaxInterpBlockEntries, "maximum loop iterations during interp (0 to disable)")
|
||||||
var tags buildutil.TagsFlag
|
var tags buildutil.TagsFlag
|
||||||
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
|
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
|
||||||
target := flag.String("target", "", "chip/board name or JSON target specification file")
|
target := flag.String("target", "", "chip/board name or JSON target specification file")
|
||||||
@@ -1855,42 +1857,43 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options := &compileopts.Options{
|
options := &compileopts.Options{
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
GOARM: goenv.Get("GOARM"),
|
GOARM: goenv.Get("GOARM"),
|
||||||
GOMIPS: goenv.Get("GOMIPS"),
|
GOMIPS: goenv.Get("GOMIPS"),
|
||||||
Target: *target,
|
Target: *target,
|
||||||
BuildMode: *buildMode,
|
BuildMode: *buildMode,
|
||||||
StackSize: stackSize,
|
StackSize: stackSize,
|
||||||
Opt: *opt,
|
Opt: *opt,
|
||||||
GC: *gc,
|
GC: *gc,
|
||||||
PanicStrategy: *panicStrategy,
|
PanicStrategy: *panicStrategy,
|
||||||
Scheduler: *scheduler,
|
Scheduler: *scheduler,
|
||||||
Serial: *serial,
|
Serial: *serial,
|
||||||
Work: *work,
|
Work: *work,
|
||||||
InterpTimeout: *interpTimeout,
|
InterpTimeout: *interpTimeout,
|
||||||
PrintIR: *printIR,
|
InterpMaxLoopIterations: *interpLoopLimit,
|
||||||
DumpSSA: *dumpSSA,
|
PrintIR: *printIR,
|
||||||
VerifyIR: *verifyIR,
|
DumpSSA: *dumpSSA,
|
||||||
SkipDWARF: *skipDwarf,
|
VerifyIR: *verifyIR,
|
||||||
Semaphore: make(chan struct{}, *parallelism),
|
SkipDWARF: *skipDwarf,
|
||||||
Debug: !*nodebug,
|
Semaphore: make(chan struct{}, *parallelism),
|
||||||
Nobounds: *nobounds,
|
Debug: !*nodebug,
|
||||||
PrintSizes: *printSize,
|
Nobounds: *nobounds,
|
||||||
PrintStacks: *printStacks,
|
PrintSizes: *printSize,
|
||||||
PrintAllocs: printAllocs,
|
PrintStacks: *printStacks,
|
||||||
Tags: []string(tags),
|
PrintAllocs: printAllocs,
|
||||||
TestConfig: testConfig,
|
Tags: []string(tags),
|
||||||
GlobalValues: globalVarValues,
|
TestConfig: testConfig,
|
||||||
Programmer: *programmer,
|
GlobalValues: globalVarValues,
|
||||||
OpenOCDCommands: ocdCommands,
|
Programmer: *programmer,
|
||||||
LLVMFeatures: *llvmFeatures,
|
OpenOCDCommands: ocdCommands,
|
||||||
Monitor: *monitor,
|
LLVMFeatures: *llvmFeatures,
|
||||||
BaudRate: *baudrate,
|
Monitor: *monitor,
|
||||||
Timeout: *timeout,
|
BaudRate: *baudrate,
|
||||||
WITPackage: witPackage,
|
Timeout: *timeout,
|
||||||
WITWorld: witWorld,
|
WITPackage: witPackage,
|
||||||
GoCompatibility: *gocompatibility,
|
WITWorld: witWorld,
|
||||||
|
GoCompatibility: *gocompatibility,
|
||||||
}
|
}
|
||||||
if *printCommands {
|
if *printCommands {
|
||||||
options.PrintCommands = printCommand
|
options.PrintCommands = printCommand
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ func TestBuild(t *testing.T) {
|
|||||||
"interface.go",
|
"interface.go",
|
||||||
"json.go",
|
"json.go",
|
||||||
"map.go",
|
"map.go",
|
||||||
|
"map_bigkey.go",
|
||||||
"math.go",
|
"math.go",
|
||||||
"oldgo/",
|
"oldgo/",
|
||||||
"print.go",
|
"print.go",
|
||||||
@@ -155,6 +156,19 @@ func TestBuild(t *testing.T) {
|
|||||||
}
|
}
|
||||||
runTestWithConfig("ldflags.go", t, opts, nil, nil)
|
runTestWithConfig("ldflags.go", t, opts, nil, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Same as ldflags, but the global has a default value in source.
|
||||||
|
// -ldflags -X must override it, matching standard Go behaviour.
|
||||||
|
t.Run("ldflags-initialized", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
opts := optionsFromTarget("", sema)
|
||||||
|
opts.GlobalValues = map[string]map[string]string{
|
||||||
|
"main": {
|
||||||
|
"someGlobal": "foobar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runTestWithConfig("ldflags-initialized.go", t, opts, nil, nil)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
@@ -283,6 +297,11 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
// limited amount of memory.
|
// limited amount of memory.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
case "map_bigkey.go":
|
||||||
|
// Compiler generates many large stack temporaries for [256]byte
|
||||||
|
// map keys, overflowing the goroutine stack (384 bytes).
|
||||||
|
continue
|
||||||
|
|
||||||
case "gc.go":
|
case "gc.go":
|
||||||
// Does not pass due to high mark false positive rate.
|
// Does not pass due to high mark false positive rate.
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -453,10 +453,7 @@ func rawStructFieldFromPointer(descriptor *structType, fieldType *RawType, data
|
|||||||
data = unsafe.Add(data, 1) // C: data+1
|
data = unsafe.Add(data, 1) // C: data+1
|
||||||
tagLen := uintptr(*(*byte)(data))
|
tagLen := uintptr(*(*byte)(data))
|
||||||
data = unsafe.Add(data, 1) // C: data+1
|
data = unsafe.Add(data, 1) // C: data+1
|
||||||
tag = *(*string)(unsafe.Pointer(&stringHeader{
|
tag = unsafe.String((*byte)(data), tagLen)
|
||||||
data: data,
|
|
||||||
len: tagLen,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the PkgPath to some (arbitrary) value if the package path is not
|
// Set the PkgPath to some (arbitrary) value if the package path is not
|
||||||
@@ -930,10 +927,7 @@ func readStringZ(data unsafe.Pointer) string {
|
|||||||
data = unsafe.Add(data, 1) // C: data++
|
data = unsafe.Add(data, 1) // C: data++
|
||||||
}
|
}
|
||||||
|
|
||||||
return *(*string)(unsafe.Pointer(&stringHeader{
|
return unsafe.String((*byte)(start), len)
|
||||||
data: start,
|
|
||||||
len: len,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RawType) name() string {
|
func (t *RawType) name() string {
|
||||||
|
|||||||
@@ -146,6 +146,19 @@ func TypeAssert[T any](v Value) (T, bool) {
|
|||||||
|
|
||||||
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
|
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
|
||||||
// be subject to the isExported check.
|
// be subject to the isExported check.
|
||||||
|
// loadSmallValue loads a value of size <= sizeof(uintptr) from ptr into
|
||||||
|
// a pointer-sized value suitable for storing in an interface's data field.
|
||||||
|
func loadSmallValue(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||||
|
if size == unsafe.Sizeof(uintptr(0)) {
|
||||||
|
return *(*unsafe.Pointer)(ptr)
|
||||||
|
}
|
||||||
|
var value uintptr
|
||||||
|
for j := size; j != 0; j-- {
|
||||||
|
value = (value << 8) | uintptr(*(*uint8)(unsafe.Add(ptr, j-1)))
|
||||||
|
}
|
||||||
|
return unsafe.Pointer(value)
|
||||||
|
}
|
||||||
|
|
||||||
func valueInterfaceUnsafe(v Value) interface{} {
|
func valueInterfaceUnsafe(v Value) interface{} {
|
||||||
if v.typecode.Kind() == Interface {
|
if v.typecode.Kind() == Interface {
|
||||||
// The value itself is an interface. This can happen when getting the
|
// The value itself is an interface. This can happen when getting the
|
||||||
@@ -158,11 +171,7 @@ func valueInterfaceUnsafe(v Value) interface{} {
|
|||||||
if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||||
// Value was indirect but must be put back directly in the interface
|
// Value was indirect but must be put back directly in the interface
|
||||||
// value.
|
// value.
|
||||||
var value uintptr
|
v.value = loadSmallValue(v.value, v.typecode.Size())
|
||||||
for j := v.typecode.Size(); j != 0; j-- {
|
|
||||||
value = (value << 8) | uintptr(*(*uint8)(unsafe.Add(v.value, j-1)))
|
|
||||||
}
|
|
||||||
v.value = unsafe.Pointer(value)
|
|
||||||
}
|
}
|
||||||
return composeInterface(unsafe.Pointer(v.typecode), v.value)
|
return composeInterface(unsafe.Pointer(v.typecode), v.value)
|
||||||
}
|
}
|
||||||
@@ -713,20 +722,12 @@ func (v Value) Slice(i, j int) Value {
|
|||||||
|
|
||||||
case String:
|
case String:
|
||||||
i, j := uintptr(i), uintptr(j)
|
i, j := uintptr(i), uintptr(j)
|
||||||
str := *(*stringHeader)(v.value)
|
str := *(*string)(v.value)
|
||||||
|
sliced := str[i:j]
|
||||||
if j < i || str.len < j {
|
|
||||||
slicePanic()
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr := stringHeader{
|
|
||||||
data: unsafe.Add(str.data, i),
|
|
||||||
len: j - i,
|
|
||||||
}
|
|
||||||
|
|
||||||
return Value{
|
return Value{
|
||||||
typecode: v.typecode,
|
typecode: v.typecode,
|
||||||
value: unsafe.Pointer(&hdr),
|
value: unsafe.Pointer(&sliced),
|
||||||
flags: v.flags,
|
flags: v.flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -801,7 +802,7 @@ func (v Value) Len() int {
|
|||||||
case Slice:
|
case Slice:
|
||||||
return int((*sliceHeader)(v.value).len)
|
return int((*sliceHeader)(v.value).len)
|
||||||
case String:
|
case String:
|
||||||
return int((*stringHeader)(v.value).len)
|
return len(*(*string)(v.value))
|
||||||
default:
|
default:
|
||||||
panic(&ValueError{Method: "Len", Kind: v.Kind()})
|
panic(&ValueError{Method: "Len", Kind: v.Kind()})
|
||||||
}
|
}
|
||||||
@@ -977,13 +978,10 @@ func (v Value) Index(i int) Value {
|
|||||||
// Keeping valueFlagExported if set, but don't set valueFlagIndirect
|
// Keeping valueFlagExported if set, but don't set valueFlagIndirect
|
||||||
// otherwise CanSet will return true for string elements (which is bad,
|
// otherwise CanSet will return true for string elements (which is bad,
|
||||||
// strings are read-only).
|
// strings are read-only).
|
||||||
s := *(*stringHeader)(v.value)
|
s := *(*string)(v.value)
|
||||||
if uint(i) >= uint(s.len) {
|
|
||||||
panic("reflect: string index out of range")
|
|
||||||
}
|
|
||||||
return Value{
|
return Value{
|
||||||
typecode: uint8Type,
|
typecode: uint8Type,
|
||||||
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Add(s.data, i)))),
|
value: unsafe.Pointer(uintptr(s[i])),
|
||||||
flags: v.flags & valueFlagExported,
|
flags: v.flags & valueFlagExported,
|
||||||
}
|
}
|
||||||
case Array:
|
case Array:
|
||||||
@@ -1085,18 +1083,8 @@ func (v Value) MapKeys() []Value {
|
|||||||
k := New(v.typecode.Key())
|
k := New(v.typecode.Key())
|
||||||
e := New(v.typecode.Elem())
|
e := New(v.typecode.Elem())
|
||||||
|
|
||||||
keyType := v.typecode.key()
|
|
||||||
keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
|
|
||||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
|
|
||||||
|
|
||||||
for hashmapNext(v.pointer(), it, k.value, e.value) {
|
for hashmapNext(v.pointer(), it, k.value, e.value) {
|
||||||
if shouldUnpackInterface {
|
keys = append(keys, k.Elem())
|
||||||
intf := *(*interface{})(k.value)
|
|
||||||
v := ValueOf(intf)
|
|
||||||
keys = append(keys, v)
|
|
||||||
} else {
|
|
||||||
keys = append(keys, k.Elem())
|
|
||||||
}
|
|
||||||
k = New(v.typecode.Key())
|
k = New(v.typecode.Key())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1109,9 +1097,40 @@ func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueS
|
|||||||
//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet
|
//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet
|
||||||
func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
|
func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
|
||||||
|
|
||||||
//go:linkname hashmapInterfaceGet runtime.hashmapInterfaceGet
|
//go:linkname hashmapGenericGet runtime.hashmapGenericGet
|
||||||
func hashmapInterfaceGet(m unsafe.Pointer, key interface{}, value unsafe.Pointer, valueSize uintptr) bool
|
func hashmapGenericGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
|
||||||
|
|
||||||
|
// genericKeyPtr returns a pointer to key data suitable for passing to the
|
||||||
|
// hashmapGeneric* functions. When the map's key type is an interface,
|
||||||
|
// special handling is needed: if the key Value already holds an interface
|
||||||
|
// (e.g. from MapKeys iteration), its memory already contains the
|
||||||
|
// {typecode, data} pair the hashmap expects, so we use it directly.
|
||||||
|
// If the key is a concrete type being assigned to an interface-keyed map,
|
||||||
|
// we compose the interface first.
|
||||||
|
func genericKeyPtr(vkey *RawType, key Value) unsafe.Pointer {
|
||||||
|
if vkey.Kind() == Interface {
|
||||||
|
if key.Kind() == Interface {
|
||||||
|
// Key is already an interface value stored indirectly;
|
||||||
|
// key.value points to {typecode, data}.
|
||||||
|
return key.value
|
||||||
|
}
|
||||||
|
// Concrete value being used as an interface key.
|
||||||
|
// For small addressable values, key.value is a pointer to
|
||||||
|
// the data, but the interface value field stores the data
|
||||||
|
// directly; load it using the same endian-safe approach as
|
||||||
|
// valueInterfaceUnsafe.
|
||||||
|
val := key.value
|
||||||
|
if key.isIndirect() && key.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||||
|
val = loadSmallValue(key.value, key.typecode.Size())
|
||||||
|
}
|
||||||
|
intf := composeInterface(unsafe.Pointer(key.typecode), val)
|
||||||
|
return unsafe.Pointer(&intf)
|
||||||
|
}
|
||||||
|
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||||
|
return key.value
|
||||||
|
}
|
||||||
|
return unsafe.Pointer(&key.value)
|
||||||
|
}
|
||||||
func (v Value) MapIndex(key Value) Value {
|
func (v Value) MapIndex(key Value) Value {
|
||||||
if v.Kind() != Map {
|
if v.Kind() != Map {
|
||||||
panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
|
panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
|
||||||
@@ -1140,13 +1159,16 @@ func (v Value) MapIndex(key Value) Value {
|
|||||||
} else {
|
} else {
|
||||||
keyptr = unsafe.Pointer(&key.value)
|
keyptr = unsafe.Pointer(&key.value)
|
||||||
}
|
}
|
||||||
//TODO(dgryski): zero out padding bytes in key, if any
|
|
||||||
if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
|
if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
|
||||||
return Value{}
|
return Value{}
|
||||||
}
|
}
|
||||||
return elem.Elem()
|
return elem.Elem()
|
||||||
} else {
|
} else {
|
||||||
if ok := hashmapInterfaceGet(v.pointer(), key.Interface(), elem.value, elemType.Size()); !ok {
|
// Compiler-generated hash/equal path: keys are stored at their
|
||||||
|
// actual type. Use hashmapGenericGet which dispatches through the
|
||||||
|
// map's own keyHash/keyEqual function pointers.
|
||||||
|
keyptr := genericKeyPtr(vkey, key)
|
||||||
|
if ok := hashmapGenericGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
|
||||||
return Value{}
|
return Value{}
|
||||||
}
|
}
|
||||||
return elem.Elem()
|
return elem.Elem()
|
||||||
@@ -1171,8 +1193,7 @@ type MapIter struct {
|
|||||||
key Value
|
key Value
|
||||||
val Value
|
val Value
|
||||||
|
|
||||||
valid bool
|
valid bool
|
||||||
unpackKeyInterface bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *MapIter) Key() Value {
|
func (it *MapIter) Key() Value {
|
||||||
@@ -1180,12 +1201,6 @@ func (it *MapIter) Key() Value {
|
|||||||
panic("reflect.MapIter.Key called on invalid iterator")
|
panic("reflect.MapIter.Key called on invalid iterator")
|
||||||
}
|
}
|
||||||
|
|
||||||
if it.unpackKeyInterface {
|
|
||||||
intf := *(*interface{})(it.key.value)
|
|
||||||
v := ValueOf(intf)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
return it.key.Elem()
|
return it.key.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1218,15 +1233,9 @@ func (iter *MapIter) Reset(v Value) {
|
|||||||
panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
|
panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
|
||||||
}
|
}
|
||||||
|
|
||||||
keyType := v.typecode.key()
|
|
||||||
|
|
||||||
keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
|
|
||||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
|
|
||||||
|
|
||||||
*iter = MapIter{
|
*iter = MapIter{
|
||||||
m: v,
|
m: v,
|
||||||
it: hashmapNewIterator(),
|
it: hashmapNewIterator(),
|
||||||
unpackKeyInterface: shouldUnpackInterface,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1714,8 +1723,7 @@ const zerobufferLen = 32
|
|||||||
func init() {
|
func init() {
|
||||||
// 32 characters of zero bytes
|
// 32 characters of zero bytes
|
||||||
zerobufferStr := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
zerobufferStr := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
s := (*stringHeader)(unsafe.Pointer(&zerobufferStr))
|
zerobuffer = unsafe.Pointer(unsafe.StringData(zerobufferStr))
|
||||||
zerobuffer = s.data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Zero(typ Type) Value {
|
func Zero(typ Type) Value {
|
||||||
@@ -1766,19 +1774,11 @@ type sliceHeader struct {
|
|||||||
cap uintptr
|
cap uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like sliceHeader, this type is used internally to make sure pointer and
|
// Verify SliceHeader size.
|
||||||
// non-pointer fields match those of actual strings.
|
|
||||||
type stringHeader struct {
|
|
||||||
data unsafe.Pointer
|
|
||||||
len uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify SliceHeader and StringHeader sizes.
|
|
||||||
// See https://github.com/tinygo-org/tinygo/pull/4156
|
// See https://github.com/tinygo-org/tinygo/pull/4156
|
||||||
// and https://github.com/tinygo-org/tinygo/issues/1284.
|
// and https://github.com/tinygo-org/tinygo/issues/1284.
|
||||||
var (
|
var (
|
||||||
_ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(sliceHeader{})]byte{}
|
_ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(sliceHeader{})]byte{}
|
||||||
_ [unsafe.Sizeof("")]byte = [unsafe.Sizeof(stringHeader{})]byte{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ValueError struct {
|
type ValueError struct {
|
||||||
@@ -1845,29 +1845,29 @@ func Copy(dst, src Value) int {
|
|||||||
|
|
||||||
func buflen(v Value) (unsafe.Pointer, uintptr) {
|
func buflen(v Value) (unsafe.Pointer, uintptr) {
|
||||||
var buf unsafe.Pointer
|
var buf unsafe.Pointer
|
||||||
var len uintptr
|
var length uintptr
|
||||||
switch v.typecode.Kind() {
|
switch v.typecode.Kind() {
|
||||||
case Slice:
|
case Slice:
|
||||||
hdr := (*sliceHeader)(v.value)
|
hdr := (*sliceHeader)(v.value)
|
||||||
buf = hdr.data
|
buf = hdr.data
|
||||||
len = hdr.len
|
length = hdr.len
|
||||||
case Array:
|
case Array:
|
||||||
if v.isIndirect() || v.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || v.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||||
buf = v.value
|
buf = v.value
|
||||||
} else {
|
} else {
|
||||||
buf = unsafe.Pointer(&v.value)
|
buf = unsafe.Pointer(&v.value)
|
||||||
}
|
}
|
||||||
len = uintptr(v.Len())
|
length = uintptr(v.Len())
|
||||||
case String:
|
case String:
|
||||||
hdr := (*stringHeader)(v.value)
|
s := *(*string)(v.value)
|
||||||
buf = hdr.data
|
buf = unsafe.Pointer(unsafe.StringData(s))
|
||||||
len = hdr.len
|
length = uintptr(len(s))
|
||||||
default:
|
default:
|
||||||
// This shouldn't happen
|
// This shouldn't happen
|
||||||
panic("reflect.Copy: not slice or array or string")
|
panic("reflect.Copy: not slice or array or string")
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, len
|
return buf, length
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname sliceGrow runtime.sliceGrow
|
//go:linkname sliceGrow runtime.sliceGrow
|
||||||
@@ -1969,8 +1969,8 @@ func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer)
|
|||||||
//go:linkname hashmapBinarySet runtime.hashmapBinarySet
|
//go:linkname hashmapBinarySet runtime.hashmapBinarySet
|
||||||
func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer)
|
func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer)
|
||||||
|
|
||||||
//go:linkname hashmapInterfaceSet runtime.hashmapInterfaceSet
|
//go:linkname hashmapGenericSet runtime.hashmapGenericSet
|
||||||
func hashmapInterfaceSet(m unsafe.Pointer, key interface{}, value unsafe.Pointer)
|
func hashmapGenericSet(m unsafe.Pointer, key, value unsafe.Pointer)
|
||||||
|
|
||||||
//go:linkname hashmapStringDelete runtime.hashmapStringDelete
|
//go:linkname hashmapStringDelete runtime.hashmapStringDelete
|
||||||
func hashmapStringDelete(m unsafe.Pointer, key string)
|
func hashmapStringDelete(m unsafe.Pointer, key string)
|
||||||
@@ -1978,8 +1978,8 @@ func hashmapStringDelete(m unsafe.Pointer, key string)
|
|||||||
//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDelete
|
//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDelete
|
||||||
func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer)
|
func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer)
|
||||||
|
|
||||||
//go:linkname hashmapInterfaceDelete runtime.hashmapInterfaceDelete
|
//go:linkname hashmapGenericDelete runtime.hashmapGenericDelete
|
||||||
func hashmapInterfaceDelete(m unsafe.Pointer, key interface{})
|
func hashmapGenericDelete(m unsafe.Pointer, key unsafe.Pointer)
|
||||||
|
|
||||||
func (v Value) SetMapIndex(key, elem Value) {
|
func (v Value) SetMapIndex(key, elem Value) {
|
||||||
v.checkRO()
|
v.checkRO()
|
||||||
@@ -2002,15 +2002,19 @@ func (v Value) SetMapIndex(key, elem Value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make elem an interface if it needs to be converted
|
// make elem an interface if it needs to be converted
|
||||||
if v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
|
if !del && v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
|
||||||
intf := composeInterface(unsafe.Pointer(elem.typecode), elem.value)
|
val := elem.value
|
||||||
|
if elem.isIndirect() && elem.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||||
|
val = loadSmallValue(elem.value, elem.typecode.Size())
|
||||||
|
}
|
||||||
|
intf := composeInterface(unsafe.Pointer(elem.typecode), val)
|
||||||
elem = Value{
|
elem = Value{
|
||||||
typecode: v.typecode.elem(),
|
typecode: v.typecode.elem(),
|
||||||
value: unsafe.Pointer(&intf),
|
value: unsafe.Pointer(&intf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if key.Kind() == String {
|
if vkey.Kind() == String {
|
||||||
if del {
|
if del {
|
||||||
hashmapStringDelete(v.pointer(), *(*string)(key.value))
|
hashmapStringDelete(v.pointer(), *(*string)(key.value))
|
||||||
} else {
|
} else {
|
||||||
@@ -2023,7 +2027,7 @@ func (v Value) SetMapIndex(key, elem Value) {
|
|||||||
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
|
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if key.typecode.isBinary() {
|
} else if vkey.isBinary() {
|
||||||
var keyptr unsafe.Pointer
|
var keyptr unsafe.Pointer
|
||||||
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||||
keyptr = key.value
|
keyptr = key.value
|
||||||
@@ -2043,8 +2047,11 @@ func (v Value) SetMapIndex(key, elem Value) {
|
|||||||
hashmapBinarySet(v.pointer(), keyptr, elemptr)
|
hashmapBinarySet(v.pointer(), keyptr, elemptr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Compiler-generated hash/equal path.
|
||||||
|
keyptr := genericKeyPtr(vkey, key)
|
||||||
|
|
||||||
if del {
|
if del {
|
||||||
hashmapInterfaceDelete(v.pointer(), key.Interface())
|
hashmapGenericDelete(v.pointer(), keyptr)
|
||||||
} else {
|
} else {
|
||||||
var elemptr unsafe.Pointer
|
var elemptr unsafe.Pointer
|
||||||
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||||
@@ -2053,7 +2060,7 @@ func (v Value) SetMapIndex(key, elem Value) {
|
|||||||
elemptr = unsafe.Pointer(&elem.value)
|
elemptr = unsafe.Pointer(&elem.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
hashmapInterfaceSet(v.pointer(), key.Interface(), elemptr)
|
hashmapGenericSet(v.pointer(), keyptr, elemptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2110,6 +2117,9 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value {
|
|||||||
//go:linkname hashmapMake runtime.hashmapMake
|
//go:linkname hashmapMake runtime.hashmapMake
|
||||||
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer
|
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname hashmapMakeReflect runtime.hashmapMakeReflect
|
||||||
|
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
// MakeMapWithSize creates a new map with the specified type and initial space
|
// MakeMapWithSize creates a new map with the specified type and initial space
|
||||||
// for approximately n elements.
|
// for approximately n elements.
|
||||||
func MakeMapWithSize(typ Type, n int) Value {
|
func MakeMapWithSize(typ Type, n int) Value {
|
||||||
@@ -2118,7 +2128,6 @@ func MakeMapWithSize(typ Type, n int) Value {
|
|||||||
const (
|
const (
|
||||||
hashmapAlgorithmBinary uint8 = iota
|
hashmapAlgorithmBinary uint8 = iota
|
||||||
hashmapAlgorithmString
|
hashmapAlgorithmString
|
||||||
hashmapAlgorithmInterface
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if typ.Kind() != Map {
|
if typ.Kind() != Map {
|
||||||
@@ -2132,18 +2141,19 @@ func MakeMapWithSize(typ Type, n int) Value {
|
|||||||
key := typ.Key().(*RawType)
|
key := typ.Key().(*RawType)
|
||||||
val := typ.Elem().(*RawType)
|
val := typ.Elem().(*RawType)
|
||||||
|
|
||||||
var alg uint8
|
var m unsafe.Pointer
|
||||||
|
|
||||||
if key.Kind() == String {
|
if key.Kind() == String {
|
||||||
alg = hashmapAlgorithmString
|
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmString)
|
||||||
} else if key.isBinary() {
|
} else if key.isBinary() {
|
||||||
alg = hashmapAlgorithmBinary
|
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmBinary)
|
||||||
} else {
|
} else {
|
||||||
alg = hashmapAlgorithmInterface
|
// Composite key type (struct with strings, floats, etc.).
|
||||||
|
// Use runtime-generated hash/equal closures that walk the
|
||||||
|
// type structure, matching the compiler-generated functions.
|
||||||
|
m = hashmapMakeReflect(key.Size(), val.Size(), uintptr(n), unsafe.Pointer(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg)
|
|
||||||
|
|
||||||
return Value{
|
return Value{
|
||||||
typecode: typ.(*RawType),
|
typecode: typ.(*RawType),
|
||||||
value: m,
|
value: m,
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime/interrupt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A waiter waits for an interrupt to fire. Call Wait() from a goroutine to
|
||||||
|
// pause it, and call Resume() from an interrupt handler to resume the
|
||||||
|
// goroutine.
|
||||||
|
type Waiter struct {
|
||||||
|
waiting *Task
|
||||||
|
lock PMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var resumeTaskSentinel = (*Task)(unsafe.Pointer(uintptr(2)))
|
||||||
|
var workingTaskSentinel = (*Task)(unsafe.Pointer(uintptr(1)))
|
||||||
|
|
||||||
|
// Wait from a main goroutine until an interrupt calls Resume(). It will also
|
||||||
|
// return if the interrupt called Resume() before the Wait() call (to avoid a
|
||||||
|
// race condition).
|
||||||
|
func (w *Waiter) Wait() {
|
||||||
|
if interrupt.In() {
|
||||||
|
runtimePanic("Waiter: called Wait from interrupt")
|
||||||
|
}
|
||||||
|
|
||||||
|
mask := interrupt.Disable()
|
||||||
|
w.lock.Lock()
|
||||||
|
switch w.waiting {
|
||||||
|
case nil, workingTaskSentinel:
|
||||||
|
w.waiting = Current()
|
||||||
|
w.lock.Unlock()
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
Pause()
|
||||||
|
case resumeTaskSentinel:
|
||||||
|
// Marked as 'resume now', can return immediately.
|
||||||
|
w.waiting = workingTaskSentinel
|
||||||
|
w.lock.Unlock()
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
default:
|
||||||
|
w.lock.Unlock()
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
runtimePanic("Waiter: task is waiting already")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume a waiting goroutine from an interrupt handler. If there is a goroutine
|
||||||
|
// waiting, it will resume. If not, the next one that calls Wait() will
|
||||||
|
// immediately resume.
|
||||||
|
func (w *Waiter) Resume() {
|
||||||
|
if !interrupt.In() {
|
||||||
|
runtimePanic("Waiter: called Resume outside interrupt")
|
||||||
|
}
|
||||||
|
|
||||||
|
waiting := w.waiting
|
||||||
|
switch waiting {
|
||||||
|
case nil, workingTaskSentinel:
|
||||||
|
w.waiting = resumeTaskSentinel
|
||||||
|
case resumeTaskSentinel:
|
||||||
|
// Called Resume() twice in a row, this may indicate a bug at the caller
|
||||||
|
// site.
|
||||||
|
default:
|
||||||
|
// Schedule the given task to resume.
|
||||||
|
// If it's not yet paused, it will immediately resume on the next call
|
||||||
|
// to Pause().
|
||||||
|
w.waiting = workingTaskSentinel
|
||||||
|
scheduleTask(waiting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if Done has not been called after a Resume call.
|
||||||
|
// This is typically used to indicate the task outside the interrupt is still
|
||||||
|
// being worked on.
|
||||||
|
func (w *Waiter) Working() bool {
|
||||||
|
switch w.waiting {
|
||||||
|
case workingTaskSentinel, resumeTaskSentinel:
|
||||||
|
return true
|
||||||
|
default: // nil, <*task.Task>
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done can be called outside interrupt context to indicate the task this waiter
|
||||||
|
// was waiting for is done, and Working() can return false. It is entirely
|
||||||
|
// optional, if not called Working will continue to return true until it is
|
||||||
|
// blocked in Wait() but the waiter will function normally otherwise.
|
||||||
|
func (w *Waiter) Done() {
|
||||||
|
if interrupt.In() {
|
||||||
|
runtimePanic("Waiter: called Done from interrupt")
|
||||||
|
}
|
||||||
|
|
||||||
|
mask := interrupt.Disable()
|
||||||
|
w.lock.Lock()
|
||||||
|
if w.waiting == workingTaskSentinel {
|
||||||
|
w.waiting = nil
|
||||||
|
}
|
||||||
|
w.lock.Unlock()
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
}
|
||||||
+22
-22
@@ -1,12 +1,12 @@
|
|||||||
package msc
|
package msc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"internal/task"
|
||||||
"machine"
|
"machine"
|
||||||
"machine/usb"
|
"machine/usb"
|
||||||
"machine/usb/descriptor"
|
"machine/usb/descriptor"
|
||||||
"machine/usb/msc/csw"
|
"machine/usb/msc/csw"
|
||||||
"machine/usb/msc/scsi"
|
"machine/usb/msc/scsi"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type mscState uint8
|
type mscState uint8
|
||||||
@@ -26,14 +26,14 @@ const (
|
|||||||
var MSC *msc
|
var MSC *msc
|
||||||
|
|
||||||
type msc struct {
|
type msc struct {
|
||||||
buf []byte // Buffer for incoming/outgoing data
|
buf []byte // Buffer for incoming/outgoing data
|
||||||
blockCache []byte // Buffer for block read/write data
|
blockCache []byte // Buffer for block read/write data
|
||||||
taskQueued bool // Flag to indicate if the buffer has a task queued
|
taskWaiter task.Waiter // Waiter for events outside interrupt context
|
||||||
rxStalled bool // Flag to indicate if the RX endpoint is stalled
|
rxStalled bool // Flag to indicate if the RX endpoint is stalled
|
||||||
txStalled bool // Flag to indicate if the TX endpoint is stalled
|
txStalled bool // Flag to indicate if the TX endpoint is stalled
|
||||||
maxPacketSize uint32 // Maximum packet size for the IN endpoint
|
maxPacketSize uint32 // Maximum packet size for the IN endpoint
|
||||||
respStatus csw.Status // Response status for the last command
|
respStatus csw.Status // Response status for the last command
|
||||||
sendZLP bool // Flag to indicate if a zero-length packet should be sent before sending CSW
|
sendZLP bool // Flag to indicate if a zero-length packet should be sent before sending CSW
|
||||||
|
|
||||||
cbw *CBW // Last received Command Block Wrapper
|
cbw *CBW // Last received Command Block Wrapper
|
||||||
queuedBytes uint32 // Number of bytes queued for sending
|
queuedBytes uint32 // Number of bytes queued for sending
|
||||||
@@ -120,21 +120,21 @@ func newMSC(dev machine.BlockDevice) *msc {
|
|||||||
func (m *msc) processTasks() {
|
func (m *msc) processTasks() {
|
||||||
// Process tasks that cannot be done in an interrupt context
|
// Process tasks that cannot be done in an interrupt context
|
||||||
for {
|
for {
|
||||||
if m.taskQueued {
|
// Wait for the next task to arrive.
|
||||||
cmd := m.cbw.SCSICmd()
|
m.taskWaiter.Wait()
|
||||||
switch cmd.CmdType() {
|
|
||||||
case scsi.CmdWrite:
|
|
||||||
m.scsiWrite(cmd, m.buf)
|
|
||||||
case scsi.CmdUnmap:
|
|
||||||
m.scsiUnmap(m.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acknowledge the received data from the host
|
cmd := m.cbw.SCSICmd()
|
||||||
m.queuedBytes = 0
|
switch cmd.CmdType() {
|
||||||
m.taskQueued = false
|
case scsi.CmdWrite:
|
||||||
machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
|
m.scsiWrite(cmd, m.buf)
|
||||||
|
case scsi.CmdUnmap:
|
||||||
|
m.scsiUnmap(m.buf)
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Microsecond)
|
|
||||||
|
// Acknowledge the received data from the host
|
||||||
|
m.queuedBytes = 0
|
||||||
|
m.taskWaiter.Done()
|
||||||
|
machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the incoming data in our buffer for processing outside of interrupt context.
|
// Save the incoming data in our buffer for processing outside of interrupt context.
|
||||||
if m.taskQueued {
|
if m.taskWaiter.Working() {
|
||||||
// If we already have a full task queue we can't accept this data
|
// If we already have a full task queue we can't accept this data
|
||||||
m.sendScsiError(csw.StatusFailed, scsi.SenseAbortedCommand, scsi.SenseCodeMsgReject)
|
m.sendScsiError(csw.StatusFailed, scsi.SenseAbortedCommand, scsi.SenseCodeMsgReject)
|
||||||
return true
|
return true
|
||||||
@@ -268,14 +268,14 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
|
|||||||
case scsi.CmdWrite:
|
case scsi.CmdWrite:
|
||||||
// If we're writing data wait until we have a full write block of data that can be processed.
|
// If we're writing data wait until we have a full write block of data that can be processed.
|
||||||
if m.queuedBytes == uint32(cap(m.blockCache)) || (m.sentBytes+m.queuedBytes >= m.transferBytes) {
|
if m.queuedBytes == uint32(cap(m.blockCache)) || (m.sentBytes+m.queuedBytes >= m.transferBytes) {
|
||||||
m.taskQueued = true
|
m.taskWaiter.Resume()
|
||||||
}
|
}
|
||||||
case scsi.CmdUnmap:
|
case scsi.CmdUnmap:
|
||||||
m.taskQueued = true
|
m.taskWaiter.Resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't acknowledge the incoming data until we can process it.
|
// Don't acknowledge the incoming data until we can process it.
|
||||||
return !m.taskQueued
|
return !m.taskWaiter.Working()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *msc) sendScsiError(status csw.Status, key scsi.Sense, code scsi.SenseCode) {
|
func (m *msc) sendScsiError(status csw.Status, key scsi.Sense, code scsi.SenseCode) {
|
||||||
|
|||||||
+1
-1
Submodule src/net updated: e54965ed5e...1026408a38
@@ -1083,6 +1083,21 @@ type structWithSelfPtr struct {
|
|||||||
s string
|
s string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type deepEqualInterface interface {
|
||||||
|
Foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
type deepEqualConcrete struct {
|
||||||
|
int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (deepEqualConcrete) Foo() {}
|
||||||
|
|
||||||
|
var (
|
||||||
|
deepEqualConcrete1 = deepEqualConcrete{1}
|
||||||
|
deepEqualConcrete2 = deepEqualConcrete{2}
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
loop1 = &loop2
|
loop1 = &loop2
|
||||||
loop2 = &loop1
|
loop2 = &loop1
|
||||||
@@ -1115,6 +1130,7 @@ var deepEqualTests = []DeepEqualTest{
|
|||||||
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
||||||
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
|
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
|
||||||
{MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true},
|
{MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true},
|
||||||
|
{map[deepEqualInterface]string{deepEqualConcrete1: "a"}, map[deepEqualInterface]string{deepEqualConcrete1: "a"}, true},
|
||||||
|
|
||||||
// Inequalities
|
// Inequalities
|
||||||
{1, 2, false},
|
{1, 2, false},
|
||||||
@@ -1136,6 +1152,7 @@ var deepEqualTests = []DeepEqualTest{
|
|||||||
{fn3, fn3, false},
|
{fn3, fn3, false},
|
||||||
{[][]int{{1}}, [][]int{{2}}, false},
|
{[][]int{{1}}, [][]int{{2}}, false},
|
||||||
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
|
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
|
||||||
|
{map[deepEqualInterface]string{deepEqualConcrete1: "a"}, map[deepEqualInterface]string{deepEqualConcrete2: "a"}, false},
|
||||||
|
|
||||||
// Fun with floating point.
|
// Fun with floating point.
|
||||||
{math.NaN(), math.NaN(), false},
|
{math.NaN(), math.NaN(), false},
|
||||||
|
|||||||
+247
-68
@@ -13,16 +13,27 @@ import (
|
|||||||
|
|
||||||
// The underlying hashmap structure for Go.
|
// The underlying hashmap structure for Go.
|
||||||
type hashmap struct {
|
type hashmap struct {
|
||||||
buckets unsafe.Pointer // pointer to array of buckets
|
buckets unsafe.Pointer // pointer to array of buckets
|
||||||
seed uintptr
|
seed uintptr
|
||||||
count uintptr
|
count uintptr
|
||||||
keySize uintptr // maybe this can store the key type as well? E.g. keysize == 5 means string?
|
keySize uintptr
|
||||||
valueSize uintptr
|
valueSize uintptr
|
||||||
bucketBits uint8
|
keySlotSize uintptr // == keySize, or sizeof(ptr) if indirect
|
||||||
keyEqual func(x, y unsafe.Pointer, n uintptr) bool
|
valueSlotSize uintptr // == valueSize, or sizeof(ptr) if indirect
|
||||||
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
|
bucketBits uint8
|
||||||
|
flags uint8
|
||||||
|
keyEqual func(x, y unsafe.Pointer, n uintptr) bool
|
||||||
|
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
hashmapMaxKeySize = 128
|
||||||
|
hashmapMaxValueSize = 128
|
||||||
|
|
||||||
|
hashmapFlagIndirectKey = 1 << 0
|
||||||
|
hashmapFlagIndirectValue = 1 << 1
|
||||||
|
)
|
||||||
|
|
||||||
// A hashmap bucket. A bucket is a container of 8 key/value pairs: first the
|
// A hashmap bucket. A bucket is a container of 8 key/value pairs: first the
|
||||||
// following two entries, then the 8 keys, then the 8 values. This somewhat odd
|
// following two entries, then the 8 keys, then the 8 values. This somewhat odd
|
||||||
// ordering is to make sure the keys and values are well aligned when one of
|
// ordering is to make sure the keys and values are well aligned when one of
|
||||||
@@ -34,6 +45,48 @@ type hashmapBucket struct {
|
|||||||
// allocated but as they're of variable size they can't be shown here.
|
// allocated but as they're of variable size they can't be shown here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hashmapBucketHeaderSize is the offset in bytes from the start of a bucket to
|
||||||
|
// the first key, aligned to 8 bytes. This ensures that keys requiring 8-byte
|
||||||
|
// alignment (float64, complex128, uint64 on strict-alignment architectures
|
||||||
|
// like MIPS) are properly aligned in the bucket.
|
||||||
|
const hashmapBucketHeaderSize = (unsafe.Sizeof(hashmapBucket{}) + 7) &^ 7
|
||||||
|
|
||||||
|
// hashmapKeySlotSize returns the size of a key slot in the bucket. For indirect
|
||||||
|
// keys, this is the pointer size; otherwise the actual key size.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func hashmapKeySlotSize(m *hashmap) uintptr {
|
||||||
|
return m.keySlotSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashmapValueSlotSize returns the size of a value slot in the bucket.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func hashmapValueSlotSize(m *hashmap) uintptr {
|
||||||
|
return m.valueSlotSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashmapSlotKeyData returns a pointer to the actual key data for a given slot.
|
||||||
|
// For indirect keys, the slot contains a pointer that must be dereferenced.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func hashmapSlotKeyData(m *hashmap, slotKey unsafe.Pointer) unsafe.Pointer {
|
||||||
|
if m.flags&hashmapFlagIndirectKey != 0 {
|
||||||
|
return *(*unsafe.Pointer)(slotKey)
|
||||||
|
}
|
||||||
|
return slotKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashmapSlotValueData returns a pointer to the actual value data for a given slot.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func hashmapSlotValueData(m *hashmap, slotValue unsafe.Pointer) unsafe.Pointer {
|
||||||
|
if m.flags&hashmapFlagIndirectValue != 0 {
|
||||||
|
return *(*unsafe.Pointer)(slotValue)
|
||||||
|
}
|
||||||
|
return slotValue
|
||||||
|
}
|
||||||
|
|
||||||
type hashmapIterator struct {
|
type hashmapIterator struct {
|
||||||
buckets unsafe.Pointer // pointer to array of hashapBuckets
|
buckets unsafe.Pointer // pointer to array of hashapBuckets
|
||||||
numBuckets uintptr // length of buckets array
|
numBuckets uintptr // length of buckets array
|
||||||
@@ -66,20 +119,35 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm
|
|||||||
bucketBits++
|
bucketBits++
|
||||||
}
|
}
|
||||||
|
|
||||||
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + keySize*8 + valueSize*8
|
var flags uint8
|
||||||
|
keySlotSize := keySize
|
||||||
|
if keySize > hashmapMaxKeySize {
|
||||||
|
flags |= hashmapFlagIndirectKey
|
||||||
|
keySlotSize = unsafe.Sizeof(unsafe.Pointer(nil))
|
||||||
|
}
|
||||||
|
valueSlotSize := valueSize
|
||||||
|
if valueSize > hashmapMaxValueSize {
|
||||||
|
flags |= hashmapFlagIndirectValue
|
||||||
|
valueSlotSize = unsafe.Sizeof(unsafe.Pointer(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8
|
||||||
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
|
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
|
||||||
|
|
||||||
keyHash := hashmapKeyHashAlg(tinygo.HashmapAlgorithm(alg))
|
keyHash := hashmapKeyHashAlg(tinygo.HashmapAlgorithm(alg))
|
||||||
keyEqual := hashmapKeyEqualAlg(tinygo.HashmapAlgorithm(alg))
|
keyEqual := hashmapKeyEqualAlg(tinygo.HashmapAlgorithm(alg))
|
||||||
|
|
||||||
return &hashmap{
|
return &hashmap{
|
||||||
buckets: buckets,
|
buckets: buckets,
|
||||||
seed: uintptr(fastrand()),
|
seed: uintptr(fastrand()),
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
valueSize: valueSize,
|
valueSize: valueSize,
|
||||||
bucketBits: bucketBits,
|
keySlotSize: keySlotSize,
|
||||||
keyEqual: keyEqual,
|
valueSlotSize: valueSlotSize,
|
||||||
keyHash: keyHash,
|
bucketBits: bucketBits,
|
||||||
|
flags: flags,
|
||||||
|
keyEqual: keyEqual,
|
||||||
|
keyHash: keyHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +186,6 @@ func hashmapKeyEqualAlg(alg tinygo.HashmapAlgorithm) func(x, y unsafe.Pointer, n
|
|||||||
return memequal
|
return memequal
|
||||||
case tinygo.HashmapAlgorithmString:
|
case tinygo.HashmapAlgorithmString:
|
||||||
return hashmapStringEqual
|
return hashmapStringEqual
|
||||||
case tinygo.HashmapAlgorithmInterface:
|
|
||||||
return hashmapInterfaceEqual
|
|
||||||
default:
|
default:
|
||||||
// compiler bug :(
|
// compiler bug :(
|
||||||
return nil
|
return nil
|
||||||
@@ -132,8 +198,6 @@ func hashmapKeyHashAlg(alg tinygo.HashmapAlgorithm) func(key unsafe.Pointer, n,
|
|||||||
return hash32
|
return hash32
|
||||||
case tinygo.HashmapAlgorithmString:
|
case tinygo.HashmapAlgorithmString:
|
||||||
return hashmapStringPtrHash
|
return hashmapStringPtrHash
|
||||||
case tinygo.HashmapAlgorithmInterface:
|
|
||||||
return hashmapInterfacePtrHash
|
|
||||||
default:
|
default:
|
||||||
// compiler bug :(
|
// compiler bug :(
|
||||||
return nil
|
return nil
|
||||||
@@ -172,7 +236,7 @@ func hashmapLen(m *hashmap) int {
|
|||||||
|
|
||||||
//go:inline
|
//go:inline
|
||||||
func hashmapBucketSize(m *hashmap) uintptr {
|
func hashmapBucketSize(m *hashmap) uintptr {
|
||||||
return unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
return hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*8
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:inline
|
//go:inline
|
||||||
@@ -191,16 +255,14 @@ func hashmapBucketAddrForHash(m *hashmap, hash uint32) *hashmapBucket {
|
|||||||
|
|
||||||
//go:inline
|
//go:inline
|
||||||
func hashmapSlotKey(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
|
func hashmapSlotKey(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
|
||||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(slot)
|
slotKeyOffset := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*uintptr(slot)
|
||||||
slotKey := unsafe.Add(unsafe.Pointer(bucket), slotKeyOffset)
|
return unsafe.Add(unsafe.Pointer(bucket), slotKeyOffset)
|
||||||
return slotKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:inline
|
//go:inline
|
||||||
func hashmapSlotValue(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
|
func hashmapSlotValue(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
|
||||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(slot)
|
slotValueOffset := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*uintptr(slot)
|
||||||
slotValue := unsafe.Add(unsafe.Pointer(bucket), slotValueOffset)
|
return unsafe.Add(unsafe.Pointer(bucket), slotValueOffset)
|
||||||
return slotValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a specified key to a given value. Grow the map if necessary.
|
// Set a specified key to a given value. Grow the map if necessary.
|
||||||
@@ -234,9 +296,9 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
}
|
}
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// Could be an existing key that's the same.
|
// Could be an existing key that's the same.
|
||||||
if m.keyEqual(key, slotKey, m.keySize) {
|
if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) {
|
||||||
// found same key, replace it
|
// found same key, replace the value
|
||||||
memcpy(slotValue, value, m.valueSize)
|
hashmapStoreValue(m, slotValue, value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,11 +313,45 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
m.count++
|
m.count++
|
||||||
memcpy(emptySlotKey, key, m.keySize)
|
hashmapStoreKey(m, emptySlotKey, key)
|
||||||
memcpy(emptySlotValue, value, m.valueSize)
|
hashmapStoreValue(m, emptySlotValue, value)
|
||||||
*emptySlotTophash = tophash
|
*emptySlotTophash = tophash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hashmapStoreKey stores a key into a bucket slot, allocating backing storage
|
||||||
|
// if the key is indirect (first insert) or copying into it (shouldn't happen
|
||||||
|
// for keys, but handles it correctly).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func hashmapStoreKey(m *hashmap, slotKey, key unsafe.Pointer) {
|
||||||
|
if m.flags&hashmapFlagIndirectKey != 0 {
|
||||||
|
p := alloc(m.keySize, nil)
|
||||||
|
memcpy(p, key, m.keySize)
|
||||||
|
*(*unsafe.Pointer)(slotKey) = p
|
||||||
|
} else {
|
||||||
|
memcpy(slotKey, key, m.keySize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashmapStoreValue stores a value into a bucket slot. For indirect values,
|
||||||
|
// it allocates backing storage on first insert or copies into the existing
|
||||||
|
// backing on overwrite.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func hashmapStoreValue(m *hashmap, slotValue, value unsafe.Pointer) {
|
||||||
|
if m.flags&hashmapFlagIndirectValue != 0 {
|
||||||
|
p := *(*unsafe.Pointer)(slotValue)
|
||||||
|
if p == nil {
|
||||||
|
// First insert: allocate backing storage.
|
||||||
|
p = alloc(m.valueSize, nil)
|
||||||
|
*(*unsafe.Pointer)(slotValue) = p
|
||||||
|
}
|
||||||
|
memcpy(p, value, m.valueSize)
|
||||||
|
} else {
|
||||||
|
memcpy(slotValue, value, m.valueSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
|
// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
|
||||||
// value into the bucket, and returns a pointer to this bucket.
|
// value into the bucket, and returns a pointer to this bucket.
|
||||||
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
|
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
|
||||||
@@ -267,8 +363,8 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u
|
|||||||
slotKey := hashmapSlotKey(m, bucket, 0)
|
slotKey := hashmapSlotKey(m, bucket, 0)
|
||||||
slotValue := hashmapSlotValue(m, bucket, 0)
|
slotValue := hashmapSlotValue(m, bucket, 0)
|
||||||
m.count++
|
m.count++
|
||||||
memcpy(slotKey, key, m.keySize)
|
hashmapStoreKey(m, slotKey, key)
|
||||||
memcpy(slotValue, value, m.valueSize)
|
hashmapStoreValue(m, slotValue, value)
|
||||||
bucket.tophash[0] = tophash
|
bucket.tophash[0] = tophash
|
||||||
return bucket
|
return bucket
|
||||||
}
|
}
|
||||||
@@ -331,12 +427,12 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u
|
|||||||
for bucket != nil {
|
for bucket != nil {
|
||||||
for i := uint8(0); i < 8; i++ {
|
for i := uint8(0); i < 8; i++ {
|
||||||
slotKey := hashmapSlotKey(m, bucket, i)
|
slotKey := hashmapSlotKey(m, bucket, i)
|
||||||
slotValue := hashmapSlotValue(m, bucket, i)
|
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// This could be the key we're looking for.
|
// This could be the key we're looking for.
|
||||||
if m.keyEqual(key, slotKey, m.keySize) {
|
if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) {
|
||||||
// Found the key, copy it.
|
// Found the key, copy it.
|
||||||
memcpy(value, slotValue, m.valueSize)
|
slotValue := hashmapSlotValue(m, bucket, i)
|
||||||
|
memcpy(value, hashmapSlotValueData(m, slotValue), m.valueSize)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,13 +466,15 @@ func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32) {
|
|||||||
slotKey := hashmapSlotKey(m, bucket, i)
|
slotKey := hashmapSlotKey(m, bucket, i)
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// This could be the key we're looking for.
|
// This could be the key we're looking for.
|
||||||
if m.keyEqual(key, slotKey, m.keySize) {
|
if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) {
|
||||||
// Found the key, delete it.
|
// Found the key, delete it.
|
||||||
bucket.tophash[i] = 0
|
bucket.tophash[i] = 0
|
||||||
// Zero out the key and value so garbage collector doesn't pin the allocations.
|
// Zero out the slot so the GC won't pin the allocations.
|
||||||
memzero(slotKey, m.keySize)
|
keySlotSize := hashmapKeySlotSize(m)
|
||||||
|
memzero(slotKey, keySlotSize)
|
||||||
slotValue := hashmapSlotValue(m, bucket, i)
|
slotValue := hashmapSlotValue(m, bucket, i)
|
||||||
memzero(slotValue, m.valueSize)
|
valueSlotSize := hashmapValueSlotSize(m)
|
||||||
|
memzero(slotValue, valueSlotSize)
|
||||||
m.count--
|
m.count--
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -438,13 +536,13 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
|||||||
|
|
||||||
// Found a key.
|
// Found a key.
|
||||||
slotKey := hashmapSlotKey(m, it.bucket, it.bucketIndex)
|
slotKey := hashmapSlotKey(m, it.bucket, it.bucketIndex)
|
||||||
memcpy(key, slotKey, m.keySize)
|
memcpy(key, hashmapSlotKeyData(m, slotKey), m.keySize)
|
||||||
|
|
||||||
if it.buckets == m.buckets {
|
if it.buckets == m.buckets {
|
||||||
// Our view of the buckets is the same as the parent map.
|
// Our view of the buckets is the same as the parent map.
|
||||||
// Just copy the value we have
|
// Just copy the value we have
|
||||||
slotValue := hashmapSlotValue(m, it.bucket, it.bucketIndex)
|
slotValue := hashmapSlotValue(m, it.bucket, it.bucketIndex)
|
||||||
memcpy(value, slotValue, m.valueSize)
|
memcpy(value, hashmapSlotValueData(m, slotValue), m.valueSize)
|
||||||
it.bucketIndex++
|
it.bucketIndex++
|
||||||
} else {
|
} else {
|
||||||
it.bucketIndex++
|
it.bucketIndex++
|
||||||
@@ -491,6 +589,112 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
|||||||
hashmapDelete(m, key, hash)
|
hashmapDelete(m, key, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hashmap with compiler-generated key hash/equal functions.
|
||||||
|
// Unlike the binary path (which uses hash32/memequal), these use the
|
||||||
|
// type-specific keyHash and keyEqual function pointers stored in the hashmap
|
||||||
|
// struct. This is used for composite key types (e.g. structs containing
|
||||||
|
// strings) where the compiler generates specialized hash/equal functions.
|
||||||
|
|
||||||
|
func hashmapGenericSet(m *hashmap, key, value unsafe.Pointer) {
|
||||||
|
if m == nil {
|
||||||
|
nilMapPanic()
|
||||||
|
}
|
||||||
|
hash := m.keyHash(key, m.keySize, m.seed)
|
||||||
|
hashmapSet(m, key, value, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapGenericGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
|
||||||
|
if m == nil {
|
||||||
|
memzero(value, uintptr(valueSize))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hash := m.keyHash(key, m.keySize, m.seed)
|
||||||
|
return hashmapGet(m, key, value, valueSize, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapGenericDelete(m *hashmap, key unsafe.Pointer) {
|
||||||
|
if m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hash := m.keyHash(key, m.keySize, m.seed)
|
||||||
|
hashmapDelete(m, key, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashmapMakeGeneric creates a new hashmap with compiler-provided hash and
|
||||||
|
// equal functions. This avoids the interface/reflection path for composite
|
||||||
|
// key types like structs containing strings.
|
||||||
|
func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr,
|
||||||
|
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32,
|
||||||
|
keyEqual func(x, y unsafe.Pointer, n uintptr) bool) *hashmap {
|
||||||
|
bucketBits := uint8(0)
|
||||||
|
for hashmapHasSpaceToGrow(bucketBits) && hashmapOverLoadFactor(sizeHint, bucketBits) {
|
||||||
|
bucketBits++
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags uint8
|
||||||
|
keySlotSize := keySize
|
||||||
|
if keySize > hashmapMaxKeySize {
|
||||||
|
flags |= hashmapFlagIndirectKey
|
||||||
|
keySlotSize = unsafe.Sizeof(unsafe.Pointer(nil))
|
||||||
|
}
|
||||||
|
valueSlotSize := valueSize
|
||||||
|
if valueSize > hashmapMaxValueSize {
|
||||||
|
flags |= hashmapFlagIndirectValue
|
||||||
|
valueSlotSize = unsafe.Sizeof(unsafe.Pointer(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8
|
||||||
|
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
|
||||||
|
|
||||||
|
return &hashmap{
|
||||||
|
buckets: buckets,
|
||||||
|
seed: uintptr(fastrand()),
|
||||||
|
keySize: keySize,
|
||||||
|
valueSize: valueSize,
|
||||||
|
keySlotSize: keySlotSize,
|
||||||
|
valueSlotSize: valueSlotSize,
|
||||||
|
bucketBits: bucketBits,
|
||||||
|
flags: flags,
|
||||||
|
keyEqual: keyEqual,
|
||||||
|
keyHash: keyHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashmapMakeReflect creates a hashmap for reflect.MakeMapWithSize using
|
||||||
|
// closures that reconstruct interface{} values from raw key bytes,
|
||||||
|
// delegating to hashmapInterfaceHash for hashing and == for equality.
|
||||||
|
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) *hashmap {
|
||||||
|
t := (*reflectlite.RawType)(keyType)
|
||||||
|
if t.Kind() == reflectlite.Interface {
|
||||||
|
// Interface keys are already stored as interface values in the
|
||||||
|
// bucket; use the existing interface hash/equal directly.
|
||||||
|
return hashmapMakeGeneric(keySize, valueSize, sizeHint,
|
||||||
|
hashmapInterfacePtrHash, hashmapInterfaceEqual)
|
||||||
|
}
|
||||||
|
keyHash := func(key unsafe.Pointer, size, seed uintptr) uint32 {
|
||||||
|
return hashmapInterfaceHash(rawToInterface(t, key), seed)
|
||||||
|
}
|
||||||
|
keyEqual := func(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
|
return rawToInterface(t, x) == rawToInterface(t, y)
|
||||||
|
}
|
||||||
|
return hashmapMakeGeneric(keySize, valueSize, sizeHint, keyHash, keyEqual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawToInterface reconstructs an interface{} from raw bytes at ptr.
|
||||||
|
func rawToInterface(t *reflectlite.RawType, ptr unsafe.Pointer) interface{} {
|
||||||
|
var val unsafe.Pointer
|
||||||
|
if t.Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||||
|
val = reflectliteLoadSmallValue(ptr, t.Size())
|
||||||
|
} else {
|
||||||
|
val = ptr
|
||||||
|
}
|
||||||
|
i := composeInterface(unsafe.Pointer(t), val)
|
||||||
|
return *(*interface{})(unsafe.Pointer(&i))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname reflectliteLoadSmallValue internal/reflectlite.loadSmallValue
|
||||||
|
func reflectliteLoadSmallValue(ptr unsafe.Pointer, size uintptr) unsafe.Pointer
|
||||||
|
|
||||||
// Hashmap with string keys (a common case).
|
// Hashmap with string keys (a common case).
|
||||||
|
|
||||||
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
@@ -626,28 +830,3 @@ func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) ui
|
|||||||
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
return *(*interface{})(x) == *(*interface{})(y)
|
return *(*interface{})(x) == *(*interface{})(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
|
|
||||||
if m == nil {
|
|
||||||
nilMapPanic()
|
|
||||||
}
|
|
||||||
hash := hashmapInterfaceHash(key, m.seed)
|
|
||||||
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
|
|
||||||
if m == nil {
|
|
||||||
memzero(value, uintptr(valueSize))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hash := hashmapInterfaceHash(key, m.seed)
|
|
||||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashmapInterfaceDelete(m *hashmap, key interface{}) {
|
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hash := hashmapInterfaceHash(key, m.seed)
|
|
||||||
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ func llvm_sponentry() unsafe.Pointer
|
|||||||
//export strlen
|
//export strlen
|
||||||
func strlen(ptr unsafe.Pointer) uintptr
|
func strlen(ptr unsafe.Pointer) uintptr
|
||||||
|
|
||||||
|
// Special alloc function that should never actually be called.
|
||||||
|
// It is used instead of normal alloc in //go:noheap functions, and must either
|
||||||
|
// be optimized away or throw a linker error.
|
||||||
|
func alloc_noheap(size uintptr, layout unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
//export malloc
|
//export malloc
|
||||||
func malloc(size uintptr) unsafe.Pointer
|
func malloc(size uintptr) unsafe.Pointer
|
||||||
|
|
||||||
|
|||||||
@@ -82,3 +82,34 @@ func (m *Map) Swap(key, value any) (previous any, loaded bool) {
|
|||||||
m.m[key] = value
|
m.m[key] = value
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap swaps the old and new values for an existing key if the value
|
||||||
|
// stored in the map is equal to old.
|
||||||
|
func (m *Map) CompareAndSwap(key, old, new any) (swapped bool) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
if m.m == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value, ok := m.m[key]
|
||||||
|
if !ok || value != old {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
m.m[key] = new
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndDelete deletes the entry for key if its value is equal to old.
|
||||||
|
func (m *Map) CompareAndDelete(key, old any) (deleted bool) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
if m.m == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value, ok := m.m[key]
|
||||||
|
if !ok || value != old {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
delete(m.m, key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, e
|
|||||||
if addr == unsafe.Pointer(^uintptr(0)) {
|
if addr == unsafe.Pointer(^uintptr(0)) {
|
||||||
return nil, getErrno()
|
return nil, getErrno()
|
||||||
}
|
}
|
||||||
return (*[1 << 30]byte)(addr)[:length:length], nil
|
return unsafe.Slice((*byte)(addr), length), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Munmap(b []byte) (err error) {
|
func Munmap(b []byte) (err error) {
|
||||||
|
|||||||
@@ -13,5 +13,4 @@ type HashmapAlgorithm uint8
|
|||||||
const (
|
const (
|
||||||
HashmapAlgorithmBinary HashmapAlgorithm = iota
|
HashmapAlgorithmBinary HashmapAlgorithm = iota
|
||||||
HashmapAlgorithmString
|
HashmapAlgorithmString
|
||||||
HashmapAlgorithmInterface
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -243,6 +243,9 @@
|
|||||||
wasi_snapshot_preview1: {
|
wasi_snapshot_preview1: {
|
||||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md
|
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md
|
||||||
fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
|
fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
|
||||||
|
iovs_ptr >>>= 0;
|
||||||
|
iovs_len >>>= 0;
|
||||||
|
nwritten_ptr >>>= 0;
|
||||||
let nwritten = 0;
|
let nwritten = 0;
|
||||||
if (fd == 1) {
|
if (fd == 1) {
|
||||||
for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
|
for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
|
||||||
@@ -284,6 +287,8 @@
|
|||||||
throw wasmExit;
|
throw wasmExit;
|
||||||
},
|
},
|
||||||
random_get: (bufPtr, bufLen) => {
|
random_get: (bufPtr, bufLen) => {
|
||||||
|
bufPtr >>>= 0;
|
||||||
|
bufLen >>>= 0;
|
||||||
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
|
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
@@ -294,6 +299,13 @@
|
|||||||
return BigInt((timeOrigin + performance.now()) * 1e6);
|
return BigInt((timeOrigin + performance.now()) * 1e6);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// func getRandomData(r []byte)
|
||||||
|
"runtime.getRandomData": (slice_ptr, slice_len, slice_cap) => {
|
||||||
|
slice_ptr >>>= 0;
|
||||||
|
slice_len >>>= 0;
|
||||||
|
crypto.getRandomValues(loadSlice(slice_ptr, slice_len, slice_cap));
|
||||||
|
},
|
||||||
|
|
||||||
// func sleepTicks(timeout int64)
|
// func sleepTicks(timeout int64)
|
||||||
"runtime.sleepTicks": (timeout) => {
|
"runtime.sleepTicks": (timeout) => {
|
||||||
// Do not sleep, only reactivate scheduler after the given timeout.
|
// Do not sleep, only reactivate scheduler after the given timeout.
|
||||||
@@ -328,12 +340,15 @@
|
|||||||
// func stringVal(value string) ref
|
// func stringVal(value string) ref
|
||||||
"syscall/js.stringVal": (value_ptr, value_len) => {
|
"syscall/js.stringVal": (value_ptr, value_len) => {
|
||||||
value_ptr >>>= 0;
|
value_ptr >>>= 0;
|
||||||
|
value_len >>>= 0;
|
||||||
const s = loadString(value_ptr, value_len);
|
const s = loadString(value_ptr, value_len);
|
||||||
return boxValue(s);
|
return boxValue(s);
|
||||||
},
|
},
|
||||||
|
|
||||||
// func valueGet(v ref, p string) ref
|
// func valueGet(v ref, p string) ref
|
||||||
"syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
|
"syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
|
||||||
|
p_ptr >>>= 0;
|
||||||
|
p_len >>>= 0;
|
||||||
let prop = loadString(p_ptr, p_len);
|
let prop = loadString(p_ptr, p_len);
|
||||||
let v = unboxValue(v_ref);
|
let v = unboxValue(v_ref);
|
||||||
let result = Reflect.get(v, prop);
|
let result = Reflect.get(v, prop);
|
||||||
@@ -342,6 +357,8 @@
|
|||||||
|
|
||||||
// func valueSet(v ref, p string, x ref)
|
// func valueSet(v ref, p string, x ref)
|
||||||
"syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => {
|
"syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => {
|
||||||
|
p_ptr >>>= 0;
|
||||||
|
p_len >>>= 0;
|
||||||
const v = unboxValue(v_ref);
|
const v = unboxValue(v_ref);
|
||||||
const p = loadString(p_ptr, p_len);
|
const p = loadString(p_ptr, p_len);
|
||||||
const x = unboxValue(x_ref);
|
const x = unboxValue(x_ref);
|
||||||
@@ -350,6 +367,8 @@
|
|||||||
|
|
||||||
// func valueDelete(v ref, p string)
|
// func valueDelete(v ref, p string)
|
||||||
"syscall/js.valueDelete": (v_ref, p_ptr, p_len) => {
|
"syscall/js.valueDelete": (v_ref, p_ptr, p_len) => {
|
||||||
|
p_ptr >>>= 0;
|
||||||
|
p_len >>>= 0;
|
||||||
const v = unboxValue(v_ref);
|
const v = unboxValue(v_ref);
|
||||||
const p = loadString(p_ptr, p_len);
|
const p = loadString(p_ptr, p_len);
|
||||||
Reflect.deleteProperty(v, p);
|
Reflect.deleteProperty(v, p);
|
||||||
@@ -367,6 +386,11 @@
|
|||||||
|
|
||||||
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
||||||
"syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => {
|
"syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => {
|
||||||
|
ret_addr >>>= 0;
|
||||||
|
m_ptr >>>= 0;
|
||||||
|
m_len >>>= 0;
|
||||||
|
args_ptr >>>= 0;
|
||||||
|
args_len >>>= 0;
|
||||||
const v = unboxValue(v_ref);
|
const v = unboxValue(v_ref);
|
||||||
const name = loadString(m_ptr, m_len);
|
const name = loadString(m_ptr, m_len);
|
||||||
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
||||||
@@ -382,6 +406,9 @@
|
|||||||
|
|
||||||
// func valueInvoke(v ref, args []ref) (ref, bool)
|
// func valueInvoke(v ref, args []ref) (ref, bool)
|
||||||
"syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
|
"syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
|
||||||
|
ret_addr >>>= 0;
|
||||||
|
args_ptr >>>= 0;
|
||||||
|
args_len >>>= 0;
|
||||||
try {
|
try {
|
||||||
const v = unboxValue(v_ref);
|
const v = unboxValue(v_ref);
|
||||||
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
||||||
@@ -395,6 +422,9 @@
|
|||||||
|
|
||||||
// func valueNew(v ref, args []ref) (ref, bool)
|
// func valueNew(v ref, args []ref) (ref, bool)
|
||||||
"syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
|
"syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
|
||||||
|
ret_addr >>>= 0;
|
||||||
|
args_ptr >>>= 0;
|
||||||
|
args_len >>>= 0;
|
||||||
const v = unboxValue(v_ref);
|
const v = unboxValue(v_ref);
|
||||||
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
||||||
try {
|
try {
|
||||||
@@ -413,6 +443,7 @@
|
|||||||
|
|
||||||
// valuePrepareString(v ref) (ref, int)
|
// valuePrepareString(v ref) (ref, int)
|
||||||
"syscall/js.valuePrepareString": (ret_addr, v_ref) => {
|
"syscall/js.valuePrepareString": (ret_addr, v_ref) => {
|
||||||
|
ret_addr >>>= 0;
|
||||||
const s = String(unboxValue(v_ref));
|
const s = String(unboxValue(v_ref));
|
||||||
const str = encoder.encode(s);
|
const str = encoder.encode(s);
|
||||||
storeValue(ret_addr, str);
|
storeValue(ret_addr, str);
|
||||||
@@ -421,6 +452,8 @@
|
|||||||
|
|
||||||
// valueLoadString(v ref, b []byte)
|
// valueLoadString(v ref, b []byte)
|
||||||
"syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
|
"syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
|
||||||
|
slice_ptr >>>= 0;
|
||||||
|
slice_len >>>= 0;
|
||||||
const str = unboxValue(v_ref);
|
const str = unboxValue(v_ref);
|
||||||
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
|
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
|
||||||
},
|
},
|
||||||
@@ -432,6 +465,9 @@
|
|||||||
|
|
||||||
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
||||||
"syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, src_ref) => {
|
"syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, src_ref) => {
|
||||||
|
ret_addr >>>= 0;
|
||||||
|
dest_addr >>>= 0;
|
||||||
|
dest_len >>>= 0;
|
||||||
let num_bytes_copied_addr = ret_addr;
|
let num_bytes_copied_addr = ret_addr;
|
||||||
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
|
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
|
||||||
|
|
||||||
@@ -451,6 +487,9 @@
|
|||||||
// Originally copied from upstream Go project, then modified:
|
// Originally copied from upstream Go project, then modified:
|
||||||
// https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
|
// https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
|
||||||
"syscall/js.copyBytesToJS": (ret_addr, dst_ref, src_addr, src_len, src_cap) => {
|
"syscall/js.copyBytesToJS": (ret_addr, dst_ref, src_addr, src_len, src_cap) => {
|
||||||
|
ret_addr >>>= 0;
|
||||||
|
src_addr >>>= 0;
|
||||||
|
src_len >>>= 0;
|
||||||
let num_bytes_copied_addr = ret_addr;
|
let num_bytes_copied_addr = ret_addr;
|
||||||
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
|
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
|
||||||
|
|
||||||
|
|||||||
Vendored
+69
@@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
//go:noheap
|
||||||
|
func main() {
|
||||||
|
// This object is optimized away, and won't cause a linker failure.
|
||||||
|
var a int
|
||||||
|
add(&a)
|
||||||
|
|
||||||
|
// This object is not optimized away since its address is captured.
|
||||||
|
var b int
|
||||||
|
escape(&b)
|
||||||
|
|
||||||
|
// A simple defer won't cause a heap allocation.
|
||||||
|
defer func() {
|
||||||
|
println("once")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// A defer in a loop does causes a heap allocation.
|
||||||
|
for range 3 {
|
||||||
|
defer func() {
|
||||||
|
println("many")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This slice should not be heap-allocated.
|
||||||
|
s1 := make([]int, 5)
|
||||||
|
sum(s1)
|
||||||
|
|
||||||
|
// But this slice is.
|
||||||
|
s2 := make([]int, 5)
|
||||||
|
escape(&s2[0])
|
||||||
|
|
||||||
|
// Allocated at compile time as a global, so won't escape at runtime.
|
||||||
|
escape(globalInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(n *int) {
|
||||||
|
*n++
|
||||||
|
}
|
||||||
|
|
||||||
|
func sum(slice []int) (result int) {
|
||||||
|
for n := range slice {
|
||||||
|
result += n
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalInt = globalNewInt()
|
||||||
|
|
||||||
|
var globalAssign *int
|
||||||
|
|
||||||
|
//go:noheap
|
||||||
|
func globalNewInt() *int {
|
||||||
|
return new(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func escape(n *int) {
|
||||||
|
// Do some stuff that will definitely force a heap allocation.
|
||||||
|
// (This might be optimized in the future, in which case we need to change
|
||||||
|
// the function again).
|
||||||
|
n2 := func(n *int) *int {
|
||||||
|
return n
|
||||||
|
}(n)
|
||||||
|
println(n2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ERROR: noheap.go:10: object allocated on the heap in //go:noheap function
|
||||||
|
// ERROR: noheap.go:20: object allocated on the heap in //go:noheap function
|
||||||
|
// ERROR: noheap.go:30: object allocated on the heap in //go:noheap function
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// This global has a default value. It should be overridable via
|
||||||
|
// -ldflags="-X main.someGlobal=value" just like an uninitialized global.
|
||||||
|
var someGlobal = "default"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println("someGlobal:", someGlobal)
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
someGlobal: foobar
|
||||||
Vendored
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
// These globals can be changed using -ldflags="-X main.someGlobal=value".
|
// This global can be changed using -ldflags="-X main.someGlobal=value".
|
||||||
// At the moment, only globals without an initializer can be replaced this way.
|
|
||||||
var someGlobal string
|
var someGlobal string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
Vendored
+85
@@ -29,6 +29,14 @@ var testMapArrayKey = map[ArrayKey]int{
|
|||||||
}
|
}
|
||||||
var testmapIntInt = map[int]int{1: 1, 2: 4, 3: 9}
|
var testmapIntInt = map[int]int{1: 1, 2: 4, 3: 9}
|
||||||
|
|
||||||
|
// Package-level pointer map literals: these exercise the interp pass's
|
||||||
|
// ability to defer pointer-keyed map inserts to runtime (since pointer
|
||||||
|
// hashes can't be computed at compile time).
|
||||||
|
var testPtrMapVar1 = 42
|
||||||
|
var testPtrMapVar2 = 99
|
||||||
|
var testPtrMap = map[*int]int{&testPtrMapVar1: 1, &testPtrMapVar2: 2}
|
||||||
|
var testUnsafePtrMap = map[unsafe.Pointer]int{unsafe.Pointer(&testPtrMapVar1): 10}
|
||||||
|
|
||||||
type namedFloat struct {
|
type namedFloat struct {
|
||||||
s string
|
s string
|
||||||
f float32
|
f float32
|
||||||
@@ -130,6 +138,10 @@ func main() {
|
|||||||
|
|
||||||
mapgrow()
|
mapgrow()
|
||||||
|
|
||||||
|
nestedarraymaps()
|
||||||
|
|
||||||
|
ptrmaps()
|
||||||
|
|
||||||
interfacerehash()
|
interfacerehash()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,3 +320,76 @@ func interfacerehash() {
|
|||||||
println("no interface lookup failures")
|
println("no interface lookup failures")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nestedarraymaps() {
|
||||||
|
type nestedArrayElem struct {
|
||||||
|
x uint8
|
||||||
|
}
|
||||||
|
type nestedArrayKey struct {
|
||||||
|
a [5][5]nestedArrayElem
|
||||||
|
}
|
||||||
|
var k nestedArrayKey
|
||||||
|
k.a[4][4].x = 7
|
||||||
|
m := map[nestedArrayKey]int{k: 42}
|
||||||
|
println("nested array key:", m[k])
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptrmaps() {
|
||||||
|
// Package-level pointer map literals (interp defers inserts to runtime).
|
||||||
|
println("ptr map literal:", testPtrMap[&testPtrMapVar1], testPtrMap[&testPtrMapVar2])
|
||||||
|
println("unsafe ptr literal:", testUnsafePtrMap[unsafe.Pointer(&testPtrMapVar1)])
|
||||||
|
|
||||||
|
// Runtime pointer maps.
|
||||||
|
a, b, c := 1, 2, 3
|
||||||
|
m := make(map[*int]int)
|
||||||
|
m[&a] = 10
|
||||||
|
m[&b] = 20
|
||||||
|
m[&c] = 30
|
||||||
|
println("ptr map len:", len(m))
|
||||||
|
println("ptr map a:", m[&a])
|
||||||
|
delete(m, &b)
|
||||||
|
_, ok := m[&b]
|
||||||
|
println("ptr map deleted:", ok)
|
||||||
|
|
||||||
|
// Runtime unsafe.Pointer maps.
|
||||||
|
m2 := make(map[unsafe.Pointer]int)
|
||||||
|
m2[unsafe.Pointer(&a)] = 100
|
||||||
|
println("unsafe ptr map:", m2[unsafe.Pointer(&a)])
|
||||||
|
|
||||||
|
// Struct keys with padding: the hash/equal must operate per-field
|
||||||
|
// and not include padding bytes. Only test when padding actually
|
||||||
|
// exists (e.g., not on AVR where alignment is 1).
|
||||||
|
type paddedKey struct {
|
||||||
|
A int8
|
||||||
|
B int32
|
||||||
|
}
|
||||||
|
if unsafe.Offsetof(paddedKey{}.B) > 1 {
|
||||||
|
pm := make(map[paddedKey]int)
|
||||||
|
var pk1, pk2 paddedKey
|
||||||
|
pk1.A = 1; pk1.B = 42
|
||||||
|
pk2.A = 1; pk2.B = 42
|
||||||
|
// Poison pk2's padding byte (between A and B).
|
||||||
|
*(*byte)(unsafe.Add(unsafe.Pointer(&pk2), 1)) = 0xFF
|
||||||
|
pm[pk1] = 100
|
||||||
|
println("padded key lookup:", pm[pk2]) // 100
|
||||||
|
println("padded key equal:", pk1 == pk2) // true
|
||||||
|
} else {
|
||||||
|
// No padding on this platform; print expected output.
|
||||||
|
println("padded key lookup:", 100)
|
||||||
|
println("padded key equal:", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct keys with blank fields: blank fields are ignored in equality.
|
||||||
|
type blankKey struct {
|
||||||
|
_ int
|
||||||
|
X string
|
||||||
|
}
|
||||||
|
bm := make(map[blankKey]int)
|
||||||
|
var bk1, bk2 blankKey
|
||||||
|
bk1.X = "hello"
|
||||||
|
bk2.X = "hello"
|
||||||
|
*(*int)(unsafe.Pointer(&bk2)) = 999
|
||||||
|
bm[bk1] = 200
|
||||||
|
println("blank key lookup:", bm[bk2]) // 200
|
||||||
|
println("blank key equal:", bk1 == bk2) // true
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+11
@@ -80,4 +80,15 @@ tested growing of a map
|
|||||||
2
|
2
|
||||||
2
|
2
|
||||||
done
|
done
|
||||||
|
nested array key: 42
|
||||||
|
ptr map literal: 1 2
|
||||||
|
unsafe ptr literal: 10
|
||||||
|
ptr map len: 3
|
||||||
|
ptr map a: 10
|
||||||
|
ptr map deleted: false
|
||||||
|
unsafe ptr map: 100
|
||||||
|
padded key lookup: 100
|
||||||
|
padded key equal: true
|
||||||
|
blank key lookup: 200
|
||||||
|
blank key equal: true
|
||||||
no interface lookup failures
|
no interface lookup failures
|
||||||
|
|||||||
Vendored
+63
@@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Test maps with keys and values larger than 128 bytes, which triggers
|
||||||
|
// indirect storage in the bucket (pointers instead of inline data).
|
||||||
|
//
|
||||||
|
// This is a separate file from map.go because the compiler generates many
|
||||||
|
// large stack temporaries for map operations on [256]byte keys, which
|
||||||
|
// overflows the goroutine stack on AVR (384 bytes). AVR skips this test.
|
||||||
|
|
||||||
|
type BigKey [256]byte
|
||||||
|
type BigValue [256]byte
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Large key, small value.
|
||||||
|
m1 := make(map[BigKey]int)
|
||||||
|
var k1 BigKey
|
||||||
|
k1[0] = 1
|
||||||
|
k1[255] = 42
|
||||||
|
m1[k1] = 100
|
||||||
|
|
||||||
|
var k1same BigKey
|
||||||
|
k1same[0] = 1
|
||||||
|
k1same[255] = 42
|
||||||
|
|
||||||
|
var k1diff BigKey
|
||||||
|
k1diff[0] = 2
|
||||||
|
|
||||||
|
println("bigkey get:", m1[k1])
|
||||||
|
println("bigkey get same:", m1[k1same])
|
||||||
|
println("bigkey get diff:", m1[k1diff])
|
||||||
|
|
||||||
|
// Overwrite.
|
||||||
|
m1[k1] = 200
|
||||||
|
println("bigkey overwrite:", m1[k1])
|
||||||
|
|
||||||
|
// Small key, large value.
|
||||||
|
m2 := make(map[int]BigValue)
|
||||||
|
var v BigValue
|
||||||
|
v[0] = 7
|
||||||
|
v[255] = 99
|
||||||
|
m2[1] = v
|
||||||
|
got := m2[1]
|
||||||
|
println("bigval get:", got[0], got[255])
|
||||||
|
|
||||||
|
// Both large.
|
||||||
|
m3 := make(map[BigKey]BigValue)
|
||||||
|
m3[k1] = v
|
||||||
|
got3 := m3[k1]
|
||||||
|
println("bigboth get:", got3[0], got3[255])
|
||||||
|
|
||||||
|
// Delete.
|
||||||
|
delete(m3, k1)
|
||||||
|
got3 = m3[k1]
|
||||||
|
println("bigboth deleted:", got3[0])
|
||||||
|
|
||||||
|
// Iteration.
|
||||||
|
m1[k1diff] = 300
|
||||||
|
count := 0
|
||||||
|
for range m1 {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
println("bigkey len:", len(m1), "iterated:", count)
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
bigkey get: 100
|
||||||
|
bigkey get same: 100
|
||||||
|
bigkey get diff: 0
|
||||||
|
bigkey overwrite: 200
|
||||||
|
bigval get: 7 99
|
||||||
|
bigboth get: 7 99
|
||||||
|
bigboth deleted: 0
|
||||||
|
bigkey len: 2 iterated: 2
|
||||||
Vendored
+140
@@ -396,6 +396,24 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test for issue #3794: reflect MapIter.Key() should return a value with
|
||||||
|
// interface kind for map[interface{}] keys, not the underlying concrete kind.
|
||||||
|
{
|
||||||
|
m := make(map[interface{}]int)
|
||||||
|
m[1] = 2
|
||||||
|
m["hello"] = 3
|
||||||
|
rv := reflect.ValueOf(m)
|
||||||
|
iter := rv.MapRange()
|
||||||
|
for iter.Next() {
|
||||||
|
k := iter.Key()
|
||||||
|
if k.Kind() != reflect.Interface {
|
||||||
|
println("FAIL #3794: expected interface kind, got", k.Kind().String())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("reflect map interface key ok")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func emptyFunc() {
|
func emptyFunc() {
|
||||||
@@ -755,6 +773,9 @@ func testImplements() {
|
|||||||
// Make FooNode and BarNode implement Node with pointer receivers
|
// Make FooNode and BarNode implement Node with pointer receivers
|
||||||
// (can't add methods to local types in function, use a different approach)
|
// (can't add methods to local types in function, use a different approach)
|
||||||
testValueSetInterface()
|
testValueSetInterface()
|
||||||
|
testMakeMapCompositeKey()
|
||||||
|
testMakeMapInterfaceKey()
|
||||||
|
testMakeMapPaddedKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
type IfaceNode interface {
|
type IfaceNode interface {
|
||||||
@@ -807,3 +828,122 @@ func randuint32() uint32 {
|
|||||||
xorshift32State = xorshift32(xorshift32State)
|
xorshift32State = xorshift32(xorshift32State)
|
||||||
return xorshift32State
|
return xorshift32State
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type compositeKey struct {
|
||||||
|
S string
|
||||||
|
N int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// testMakeMapCompositeKey tests that reflect.MakeMap works correctly with
|
||||||
|
// composite key types (structs containing strings). This exercises the
|
||||||
|
// hash/equal dispatch path for maps created through reflection rather
|
||||||
|
// than by the compiler.
|
||||||
|
func testMakeMapCompositeKey() {
|
||||||
|
println("\nreflect.MakeMap composite key:")
|
||||||
|
mapType := reflect.TypeOf(map[compositeKey]int{})
|
||||||
|
m := reflect.MakeMap(mapType)
|
||||||
|
|
||||||
|
// Insert two keys that share the same string but differ in the int field.
|
||||||
|
key1 := reflect.ValueOf(compositeKey{S: "hello", N: 1})
|
||||||
|
key2 := reflect.ValueOf(compositeKey{S: "hello", N: 2})
|
||||||
|
m.SetMapIndex(key1, reflect.ValueOf(100))
|
||||||
|
m.SetMapIndex(key2, reflect.ValueOf(200))
|
||||||
|
|
||||||
|
println("len:", m.Len())
|
||||||
|
|
||||||
|
v1 := m.MapIndex(key1)
|
||||||
|
if v1.IsValid() {
|
||||||
|
println("key1:", v1.Int())
|
||||||
|
} else {
|
||||||
|
println("key1: not found")
|
||||||
|
}
|
||||||
|
v2 := m.MapIndex(key2)
|
||||||
|
if v2.IsValid() {
|
||||||
|
println("key2:", v2.Int())
|
||||||
|
} else {
|
||||||
|
println("key2: not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete key1, verify key2 remains.
|
||||||
|
m.SetMapIndex(key1, reflect.Value{})
|
||||||
|
println("after delete, len:", m.Len())
|
||||||
|
v2 = m.MapIndex(key2)
|
||||||
|
if v2.IsValid() {
|
||||||
|
println("key2 after delete:", v2.Int())
|
||||||
|
} else {
|
||||||
|
println("key2 after delete: not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testMakeMapInterfaceKey tests that reflect.MakeMap works correctly with
|
||||||
|
// interface{} key types, including cross-path usage (reflect insert,
|
||||||
|
// compiled lookup and vice versa).
|
||||||
|
func testMakeMapInterfaceKey() {
|
||||||
|
println("\nreflect.MakeMap interface key:")
|
||||||
|
mapType := reflect.TypeOf(map[interface{}]int{})
|
||||||
|
rv := reflect.MakeMap(mapType)
|
||||||
|
|
||||||
|
rv.SetMapIndex(reflect.ValueOf(42), reflect.ValueOf(100))
|
||||||
|
rv.SetMapIndex(reflect.ValueOf("hello"), reflect.ValueOf(200))
|
||||||
|
println("len:", rv.Len())
|
||||||
|
|
||||||
|
v1 := rv.MapIndex(reflect.ValueOf(42))
|
||||||
|
if v1.IsValid() {
|
||||||
|
println("42:", v1.Int())
|
||||||
|
} else {
|
||||||
|
println("42: not found")
|
||||||
|
}
|
||||||
|
v2 := rv.MapIndex(reflect.ValueOf("hello"))
|
||||||
|
if v2.IsValid() {
|
||||||
|
println("hello:", v2.Int())
|
||||||
|
} else {
|
||||||
|
println("hello: not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cross-path: use from compiled code.
|
||||||
|
m := rv.Interface().(map[interface{}]int)
|
||||||
|
println("compiled 42:", m[42])
|
||||||
|
println("compiled hello:", m["hello"])
|
||||||
|
|
||||||
|
// Addressable small value as key.
|
||||||
|
x := 99
|
||||||
|
addrVal := reflect.ValueOf(&x).Elem()
|
||||||
|
rv.SetMapIndex(addrVal, reflect.ValueOf(300))
|
||||||
|
v3 := rv.MapIndex(reflect.ValueOf(99))
|
||||||
|
if v3.IsValid() {
|
||||||
|
println("addressable 99:", v3.Int())
|
||||||
|
} else {
|
||||||
|
println("addressable 99: not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type paddedKey struct {
|
||||||
|
A int8
|
||||||
|
B int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// testMakeMapPaddedKey tests that struct keys with padding work correctly
|
||||||
|
// through reflect, using addressable values with poisoned padding bytes.
|
||||||
|
func testMakeMapPaddedKey() {
|
||||||
|
println("\nreflect.MakeMap padded key:")
|
||||||
|
var pk1, pk2 paddedKey
|
||||||
|
pk1.A = 1
|
||||||
|
pk1.B = 42
|
||||||
|
pk2.A = 1
|
||||||
|
pk2.B = 42
|
||||||
|
|
||||||
|
if unsafe.Offsetof(paddedKey{}.B) > 1 {
|
||||||
|
// Poison pk2's padding byte (between A and B).
|
||||||
|
*(*byte)(unsafe.Add(unsafe.Pointer(&pk2), 1)) = 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use addressable values so padding survives into reflect.
|
||||||
|
rm := reflect.MakeMap(reflect.TypeOf(map[paddedKey]int{}))
|
||||||
|
rm.SetMapIndex(reflect.ValueOf(&pk1).Elem(), reflect.ValueOf(100))
|
||||||
|
v := rm.MapIndex(reflect.ValueOf(&pk2).Elem())
|
||||||
|
if v.IsValid() {
|
||||||
|
println("padded lookup:", v.Int())
|
||||||
|
} else {
|
||||||
|
println("padded lookup: not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+19
@@ -503,6 +503,24 @@ value set interface:
|
|||||||
Set[0] to BarNode: 10
|
Set[0] to BarNode: 10
|
||||||
Set[1] still FooNode: 2
|
Set[1] still FooNode: 2
|
||||||
|
|
||||||
|
reflect.MakeMap composite key:
|
||||||
|
len: 2
|
||||||
|
key1: 100
|
||||||
|
key2: 200
|
||||||
|
after delete, len: 1
|
||||||
|
key2 after delete: 200
|
||||||
|
|
||||||
|
reflect.MakeMap interface key:
|
||||||
|
len: 2
|
||||||
|
42: 100
|
||||||
|
hello: 200
|
||||||
|
compiled 42: 100
|
||||||
|
compiled hello: 200
|
||||||
|
addressable 99: 300
|
||||||
|
|
||||||
|
reflect.MakeMap padded key:
|
||||||
|
padded lookup: 100
|
||||||
|
|
||||||
alignment / offset:
|
alignment / offset:
|
||||||
struct{[0]func(); byte}: true
|
struct{[0]func(); byte}: true
|
||||||
|
|
||||||
@@ -512,3 +530,4 @@ blue gopher
|
|||||||
v.Interface() method
|
v.Interface() method
|
||||||
kind: interface
|
kind: interface
|
||||||
int 5
|
int 5
|
||||||
|
reflect map interface key ok
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package mapbench
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
type compositeKey struct {
|
||||||
|
S string
|
||||||
|
N int32
|
||||||
|
}
|
||||||
|
|
||||||
|
var intSink int
|
||||||
|
|
||||||
|
func BenchmarkMapStringShortGet(b *testing.B) {
|
||||||
|
m := make(map[string]int, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
m[string(rune('A'+i%26))+string(rune('a'+i/26))] = i
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
intSink += m["Qa"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapStringLongGet(b *testing.B) {
|
||||||
|
m := make(map[string]int, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
s := "this-is-a-longer-key-for-testing-"
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
s += string(rune('A' + (i+j)%26))
|
||||||
|
}
|
||||||
|
m[s] = i
|
||||||
|
}
|
||||||
|
key := "this-is-a-longer-key-for-testing-ABC"
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
intSink += m[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapCompositeGet(b *testing.B) {
|
||||||
|
m := make(map[compositeKey]int, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
m[compositeKey{S: string(rune('A'+i%26)) + string(rune('a'+i/26)), N: int32(i)}] = i
|
||||||
|
}
|
||||||
|
key := compositeKey{S: "Qa", N: 42}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
intSink += m[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapIntGet(b *testing.B) {
|
||||||
|
m := make(map[int]int, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
m[i*7] = i
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
intSink += m[42]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapCompositeSet(b *testing.B) {
|
||||||
|
m := make(map[compositeKey]int, b.N)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
m[compositeKey{S: "key", N: int32(i)}] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type bigKey [256]byte
|
||||||
|
|
||||||
|
func BenchmarkMapBigKeyGet(b *testing.B) {
|
||||||
|
m := make(map[bigKey]int, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
var k bigKey
|
||||||
|
k[0] = byte(i)
|
||||||
|
m[k] = i
|
||||||
|
}
|
||||||
|
var k bigKey
|
||||||
|
k[0] = 42
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
intSink += m[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapBigKeySet(b *testing.B) {
|
||||||
|
m := make(map[bigKey]int, b.N)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
var k bigKey
|
||||||
|
k[0] = byte(i)
|
||||||
|
k[1] = byte(i >> 8)
|
||||||
|
k[2] = byte(i >> 16)
|
||||||
|
k[3] = byte(i >> 24)
|
||||||
|
m[k] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
-3
@@ -23,8 +23,15 @@ import (
|
|||||||
// heap allocation explanation should be printed (why the object can't be stack
|
// heap allocation explanation should be printed (why the object can't be stack
|
||||||
// allocated).
|
// allocated).
|
||||||
func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc uint64, logger func(token.Position, string)) {
|
func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc uint64, logger func(token.Position, string)) {
|
||||||
allocator := mod.NamedFunction("runtime.alloc")
|
// Find allocator functions.
|
||||||
if allocator.IsNil() {
|
var allocators []llvm.Value
|
||||||
|
for _, name := range []string{"runtime.alloc", "runtime.alloc_noheap"} {
|
||||||
|
allocator := mod.NamedFunction(name)
|
||||||
|
if !allocator.IsNil() {
|
||||||
|
allocators = append(allocators, allocator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(allocators) == 0 {
|
||||||
// nothing to optimize
|
// nothing to optimize
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -43,7 +50,13 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
|
|||||||
fmt.Fprintln(os.Stderr, "mode: set")
|
fmt.Fprintln(os.Stderr, "mode: set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, heapalloc := range getUses(allocator) {
|
// Find all allocator calls.
|
||||||
|
var heapallocs []llvm.Value
|
||||||
|
for _, allocator := range allocators {
|
||||||
|
heapallocs = append(heapallocs, getUses(allocator)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, heapalloc := range heapallocs {
|
||||||
logAllocs := printAllocs != nil && printAllocs.MatchString(heapalloc.InstructionParent().Parent().Name())
|
logAllocs := printAllocs != nil && printAllocs.MatchString(heapalloc.InstructionParent().Parent().Name())
|
||||||
if heapalloc.Operand(0).IsAConstantInt().IsNil() {
|
if heapalloc.Operand(0).IsAConstantInt().IsNil() {
|
||||||
// Do not allocate variable length arrays on the stack.
|
// Do not allocate variable length arrays on the stack.
|
||||||
|
|||||||
+28
-22
@@ -10,38 +10,44 @@ import (
|
|||||||
// maps. This has not yet been implemented, however.
|
// maps. This has not yet been implemented, however.
|
||||||
func OptimizeMaps(mod llvm.Module) {
|
func OptimizeMaps(mod llvm.Module) {
|
||||||
hashmapMake := mod.NamedFunction("runtime.hashmapMake")
|
hashmapMake := mod.NamedFunction("runtime.hashmapMake")
|
||||||
if hashmapMake.IsNil() {
|
hashmapMakeGeneric := mod.NamedFunction("runtime.hashmapMakeGeneric")
|
||||||
// nothing to optimize
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hashmapBinarySet := mod.NamedFunction("runtime.hashmapBinarySet")
|
hashmapBinarySet := mod.NamedFunction("runtime.hashmapBinarySet")
|
||||||
hashmapStringSet := mod.NamedFunction("runtime.hashmapStringSet")
|
hashmapStringSet := mod.NamedFunction("runtime.hashmapStringSet")
|
||||||
|
hashmapGenericSet := mod.NamedFunction("runtime.hashmapGenericSet")
|
||||||
|
|
||||||
for _, makeInst := range getUses(hashmapMake) {
|
optimizeMapMake := func(makeFunc llvm.Value) {
|
||||||
updateInsts := []llvm.Value{}
|
if makeFunc.IsNil() {
|
||||||
unknownUses := false // are there any uses other than setting a value?
|
return
|
||||||
|
}
|
||||||
|
for _, makeInst := range getUses(makeFunc) {
|
||||||
|
updateInsts := []llvm.Value{}
|
||||||
|
unknownUses := false
|
||||||
|
|
||||||
for _, use := range getUses(makeInst) {
|
for _, use := range getUses(makeInst) {
|
||||||
if use := use.IsACallInst(); !use.IsNil() {
|
if use := use.IsACallInst(); !use.IsNil() {
|
||||||
switch use.CalledValue() {
|
switch use.CalledValue() {
|
||||||
case hashmapBinarySet, hashmapStringSet:
|
case hashmapBinarySet, hashmapStringSet, hashmapGenericSet:
|
||||||
updateInsts = append(updateInsts, use)
|
updateInsts = append(updateInsts, use)
|
||||||
default:
|
default:
|
||||||
|
unknownUses = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
unknownUses = true
|
unknownUses = true
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
unknownUses = true
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !unknownUses {
|
if !unknownUses {
|
||||||
// This map can be entirely removed, as it is only created but never
|
// This map can be entirely removed, as it is only created
|
||||||
// used.
|
// but never used.
|
||||||
for _, inst := range updateInsts {
|
for _, inst := range updateInsts {
|
||||||
inst.EraseFromParentAsInstruction()
|
inst.EraseFromParentAsInstruction()
|
||||||
|
}
|
||||||
|
makeInst.EraseFromParentAsInstruction()
|
||||||
}
|
}
|
||||||
makeInst.EraseFromParentAsInstruction()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optimizeMapMake(hashmapMake)
|
||||||
|
optimizeMapMake(hashmapMakeGeneric)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user