Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem b49f583ab7 loader: re-enable sanity check
We used to have this, but it was disabled to work around an upstream
bug. Let's see whether it can be re-enabled.
2026-04-24 17:04:59 +02:00
68 changed files with 1233 additions and 2284 deletions
+119
View File
@@ -0,0 +1,119 @@
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
View File
@@ -1,4 +1,5 @@
build/ build/
llvm-*/ llvm-*/
.github .github
.circleci
+1 -1
View File
@@ -1,6 +1,6 @@
# TinyGo - Go compiler for small places # TinyGo - Go compiler for small places
[![Linux](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [![macOS](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [![Windows](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [![Docker](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [![Nix](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [![Linux](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [![macOS](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [![Windows](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [![Docker](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [![Nix](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
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.
+2 -14
View File
@@ -255,18 +255,6 @@ 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()
@@ -489,7 +477,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.Options.InterpMaxLoopIterations, config.DumpSSA()) err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
if err != nil { if err != nil {
return err return err
} }
@@ -1208,7 +1196,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.Options.InterpMaxLoopIterations, config.DumpSSA()) err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
if err != nil { if err != nil {
return err return err
} }
+1 -1
View File
@@ -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 := unsafe.Slice((**C.char)(buf), len(args)) cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):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
+1 -5
View File
@@ -122,16 +122,12 @@ 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: msg, Msg: "linker could not find symbol " + symbolName,
}) })
} }
} }
+3 -3
View File
@@ -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 := unsafe.Slice((**C.char)(cmdargsC), len(cflags)) cmdargs := (*[1 << 16]*C.char)(cmdargsC)
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 := unsafe.Slice((*byte)(unsafe.Pointer(rawData)), size) data := (*[1 << 24]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 := unsafe.Slice((*byte)(unsafe.Pointer(sourcePtr)), size) source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size: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' {
+39 -40
View File
@@ -21,46 +21,45 @@ 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
InterpMaxLoopIterations int PrintIR bool
PrintIR bool DumpSSA bool
DumpSSA bool VerifyIR bool
VerifyIR bool SkipDWARF bool
SkipDWARF bool PrintCommands func(cmd string, args ...string) `json:"-"`
PrintCommands func(cmd string, args ...string) `json:"-"` Semaphore chan struct{} `json:"-"` // -p flag controls cap
Semaphore chan struct{} `json:"-"` // -p flag controls cap Debug bool
Debug bool Nobounds bool
Nobounds bool PrintSizes string
PrintSizes string PrintAllocs *regexp.Regexp // regexp string
PrintAllocs *regexp.Regexp // regexp string PrintStacks bool
PrintStacks bool Tags []string
Tags []string GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value TestConfig TestConfig
TestConfig TestConfig Programmer string
Programmer string OpenOCDCommands []string
OpenOCDCommands []string LLVMFeatures string
LLVMFeatures string Monitor bool
Monitor bool BaudRate int
BaudRate int Timeout time.Duration
Timeout time.Duration WITPackage string // pass through to wasm-tools component embed invocation
WITPackage string // pass through to wasm-tools component embed invocation WITWorld string // pass through to wasm-tools component embed -w option
WITWorld string // pass through to wasm-tools component embed -w option ExtLDFlags []string
ExtLDFlags []string GoCompatibility bool // enable to check for Go version compatibility
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.
+5 -12
View File
@@ -192,16 +192,6 @@ 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
@@ -324,6 +314,9 @@ 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))
@@ -2183,7 +2176,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(b.allocFunc(), []llvm.Value{sizeValue, layoutValue}, expr.Comment) buf := b.createRuntimeCall("alloc", []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
@@ -2415,7 +2408,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(b.allocFunc(), []llvm.Value{sliceSize, layoutValue}, "makeslice.buf") slicePtr := b.createRuntimeCall("alloc", []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
-1
View File
@@ -50,7 +50,6 @@ 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
View File
@@ -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(b.allocFunc(), []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call") alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
} }
if b.NeedsStackObjects { if b.NeedsStackObjects {
b.trackPointer(alloca) b.trackPointer(alloca)
+3 -1
View File
@@ -129,9 +129,11 @@ 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)
packedAlloc := b.createRuntimeCall(b.allocFunc(), []llvm.Value{ alloc := b.mod.NamedFunction("runtime.alloc")
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 {
+157 -444
View File
@@ -3,28 +3,42 @@ 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())
llvmKeyType := b.getLLVMType(keyType) var llvmKeyType llvm.Type
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
@@ -33,42 +47,10 @@ 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) {
@@ -90,23 +72,32 @@ 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 { } else if hashmapIsBinaryKey(keyType) {
// Key stored at actual type: either binary-comparable or with // key can be compared with runtime.memequal
// compiler-generated hash/equal. // Store the key in an alloca, in the entry block to avoid dynamic stack
// 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}
fnName := "hashmapBinaryGet" commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericGet"
}
commaOkValue = b.createRuntimeCall(fnName, params, "")
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize) b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
} else {
// 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("hashmapInterfaceGet", params, "")
} }
// Load the resulting value from the hashmap. The value is set to the zero // Load the resulting value from the hashmap. The value is set to the zero
@@ -129,22 +120,29 @@ 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 { } else if hashmapIsBinaryKey(keyType) {
// Key stored at actual type. // key can be compared with runtime.memequal
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca) b.CreateStore(key, keyAlloca)
fnName := "hashmapBinarySet" b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericSet"
}
params := []llvm.Value{m, keyAlloca, valueAlloca} params := []llvm.Value{m, keyAlloca, valueAlloca}
b.createRuntimeCall(fnName, params, "") b.createRuntimeCall("hashmapBinarySet", params, "")
b.emitLifetimeEnd(keyAlloca, keySize) 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}
b.createRuntimeCall("hashmapInterfaceSet", params, "")
} }
b.emitLifetimeEnd(valueAlloca, valueSize) b.emitLifetimeEnd(valueAlloca, valueSize)
} }
@@ -152,24 +150,32 @@ 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 { } else if hashmapIsBinaryKey(keyType) {
// 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)
fnName := "hashmapBinaryDelete" b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericDelete"
}
params := []llvm.Value{m, keyAlloca} params := []llvm.Value{m, keyAlloca}
b.createRuntimeCall(fnName, params, "") b.createRuntimeCall("hashmapBinaryDelete", params, "")
b.emitLifetimeEnd(keyAlloca, keySize) b.emitLifetimeEnd(keyAlloca, keySize)
return nil 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}
b.createRuntimeCall("hashmapInterfaceDelete", params, "")
return nil
} }
} }
@@ -189,15 +195,42 @@ 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)
// All key types are now stored at their declared type (no interface wrapping). // There is a special case in which keys are stored as an interface value
// 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(llvmKeyType, "range.key") mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "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(llvmKeyType, mapKeyAlloca, "") mapKey := b.CreateLoad(llvmStoredKeyType, 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)
@@ -217,9 +250,20 @@ 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:
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 || keyType.Kind() == types.UnsafePointer // TODO: unsafe.Pointer is also a binary key, but to support that we
// 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:
@@ -227,399 +271,68 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
} }
} }
// hashmapKeyHashSignature returns the Go type signature for hashmap key hash func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
// functions: func(key unsafe.Pointer, size, seed uintptr) uint32 // We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
func hashmapKeyHashSignature() *types.Signature { // To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
return types.NewSignatureType(nil, nil, nil, // offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
types.NewTuple( // we handle nested types. Next, we determine if there are any padding bytes before the next
types.NewVar(token.NoPos, nil, "key", types.Typ[types.UnsafePointer]), // element and zero those as well.
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,
)
}
// hashmapKeyEqualSignature returns the Go type signature for hashmap key equal zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
// 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,
)
}
// hashmapKeyFuncName returns a canonical name for a generated hash or equal switch llvmType.TypeKind() {
// function based on the key type's underlying structure. Named types are case llvm.IntegerTypeKind:
// replaced with their underlying types so that structurally identical key // no padding bytes
// types (e.g., struct{i1; str1} and struct{i2; str2} where both i1, i2 are return nil
// int and str1, str2 are string) share the same generated function. case llvm.PointerTypeKind:
func hashmapKeyFuncName(prefix string, keyType types.Type) string { // mo padding bytes
return prefix + "." + hashmapCanonicalTypeName(keyType) return nil
} case llvm.ArrayTypeKind:
llvmArrayType := llvmType
llvmElemType := llvmType.ElementType()
// hashmapCanonicalTypeName returns a string representation of the hash/equal for i := 0; i < llvmArrayType.ArrayLength(); i++ {
// operations needed for a type, stripping named types where the operation does idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
// not depend on the name. Pointer and channel names do not include the element elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
// type because their hash/equal operations only use the pointer word.
func hashmapCanonicalTypeName(t types.Type) string { // zero any padding bytes in this element
switch t := t.Underlying().(type) { b.zeroUndefBytes(llvmElemType, elemPtr)
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:
if t.NumMethods() == 0 {
return "interface{}"
}
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 case llvm.StructTypeKind:
// of a key of the given type. The function is generated on first call and llvmStructType := llvmType
// cached in the module. numFields := llvmStructType.StructElementTypesCount()
func (b *builder) getOrGenerateKeyHashFunc(keyType types.Type) llvm.Value { llvmElementTypes := llvmStructType.StructElementTypes()
name := hashmapKeyFuncName("hashmapKeyHash", keyType)
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
return fn
}
// Create the LLVM function type: for i := 0; i < numFields; i++ {
// (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) idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "") elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []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() // zero any padding bytes in this field
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.body") llvmElemType := llvmElementTypes[i]
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.done") b.zeroUndefBytes(llvmElemType, elemPtr)
b.CreateBr(loopBody) // zero any padding bytes before the next field, if any
b.SetInsertPointAtEnd(loopBody) offset := b.targetData.ElementOffset(llvmStructType, i)
storeSize := b.targetData.TypeStoreSize(llvmElemType)
fieldEndOffset := offset + storeSize
phiI := b.CreatePHI(b.uintptrType, "i") var nextOffset uint64
phiHash := b.CreatePHI(b.ctx.Int32Type(), "hash.acc") if i < numFields-1 {
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
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 { } else {
floatType = b.ctx.DoubleType() // Last field? Next offset is the total size of the allocate struct.
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
} }
floatSize := b.targetData.TypeAllocSize(floatType)
imagOffset := llvm.ConstInt(b.uintptrType, floatSize, false) if fieldEndOffset != nextOffset {
// Real parts n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
xReal := b.CreateLoad(floatType, xPtr, "x.real") llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
yReal := b.CreateLoad(floatType, yPtr, "y.real") paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
realEq := b.CreateFCmp(llvm.FloatOEQ, xReal, yReal, "eq.real") b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
// 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))
} }
return nil
} }
+1 -30
View File
@@ -34,7 +34,6 @@ 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
} }
@@ -162,7 +161,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", "runtime.alloc_noheap": case "runtime.alloc":
// 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"} {
@@ -191,31 +190,6 @@ 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
@@ -477,9 +451,6 @@ 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
+33 -29
View File
@@ -10,30 +10,33 @@ 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
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
@@ -47,14 +50,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) #2 call void @runtime.divideByZeroPanic(ptr undef) #3
unreachable unreachable
} }
declare void @runtime.divideByZeroPanic(ptr) #0 declare void @runtime.divideByZeroPanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
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
@@ -64,12 +67,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) #2 call void @runtime.divideByZeroPanic(ptr undef) #3
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
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
@@ -83,12 +86,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) #2 call void @runtime.divideByZeroPanic(ptr undef) #3
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
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
@@ -98,66 +101,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) #2 call void @runtime.divideByZeroPanic(ptr undef) #3
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 { define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
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 #1 { define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
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 #1 { define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
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 #1 { define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
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
@@ -167,7 +170,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 #1 { define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
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
@@ -177,7 +180,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 #1 { define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
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
@@ -191,18 +194,19 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.foo(ptr %context) unnamed_addr #1 { define hidden void @main.foo(ptr %context) unnamed_addr #2 {
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 #1 { define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nounwind } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind }
+25 -21
View File
@@ -6,73 +6,76 @@ 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 }
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
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 #1 { define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
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) #3 call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
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) #2 declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0 declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #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) #2 declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
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) #3 %0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
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) #0 declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
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) #3 call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
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 #1 { define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
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) #3 %0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
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 #1 { define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 {
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
@@ -85,7 +88,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) #3 %select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #4
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
@@ -102,9 +105,10 @@ 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) #0 declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind } attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #4 = { nounwind }
+23 -23
View File
@@ -6,16 +6,19 @@ 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 #0 { define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
declare void @main.external(ptr) #1 declare void @main.external(ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.deferSimple(ptr %context) unnamed_addr #0 { define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
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
@@ -109,14 +112,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() #2 declare ptr @llvm.stacksave.p0() #3
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #1 declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #1 declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 { define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
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
@@ -124,14 +127,14 @@ entry:
ret void ret void
} }
declare void @runtime.printlock(ptr) #1 declare void @runtime.printlock(ptr) #2
declare void @runtime.printint32(i32, ptr) #1 declare void @runtime.printint32(i32, ptr) #2
declare void @runtime.printunlock(ptr) #1 declare void @runtime.printunlock(ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #0 { define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
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
@@ -250,7 +253,7 @@ rundefers.end7: ; preds = %rundefers.loophead1
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #0 { define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
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
@@ -259,7 +262,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #0 { define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
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
@@ -268,7 +271,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #0 { define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 {
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
@@ -312,11 +315,8 @@ 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 #0 { define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
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 #0 { define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
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 = { 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 #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 #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 #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 #2 = { nocallback nofree nosync nounwind willreturn } 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 #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 #3 = { nocallback nofree nosync nounwind willreturn }
attributes #4 = { nounwind } attributes #4 = { nounwind }
attributes #5 = { nounwind returns_twice } attributes #5 = { nounwind returns_twice }
+16 -12
View File
@@ -3,16 +3,19 @@ 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"
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 { define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #2 {
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
@@ -24,25 +27,25 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 { define hidden float @main.maxu32f(ptr %context) unnamed_addr #2 {
entry: entry:
ret float 0x41F0000000000000 ret float 0x41F0000000000000
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 { define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #2 {
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 #1 { define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #2 {
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
@@ -52,7 +55,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 { define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #2 {
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
@@ -65,7 +68,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 { define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #2 {
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
@@ -77,7 +80,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 { define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #2 {
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
@@ -90,5 +93,6 @@ entry:
ret i8 %0 ret i8 %0
} }
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
+15 -11
View File
@@ -3,44 +3,48 @@ 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"
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
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 #1 { define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #2 {
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) #2 call void %callback.funcptr(i32 3, ptr %callback.context) #3
ret void ret void
fpcall.throw: ; preds = %entry fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(ptr undef) #2 call void @runtime.nilPanic(ptr undef) #3
unreachable unreachable
} }
declare void @runtime.nilPanic(ptr) #0 declare void @runtime.nilPanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.bar(ptr %context) unnamed_addr #1 { define hidden void @main.bar(ptr %context) unnamed_addr #2 {
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 #1 { define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nounwind } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind }
+14 -14
View File
@@ -25,16 +25,19 @@ 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 { define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
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
@@ -52,11 +55,8 @@ 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 #1 { define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
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 #1 { define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
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 #1 { define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
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 #1 { define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
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 #1 { define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
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 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #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 #2 = { nounwind "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 }
+160 -78
View File
@@ -1,78 +1,144 @@
; 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-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-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%"main.Point[float32]" = type { float, float }
%"main.Point[int]" = type { i32, i32 } %"main.Point[int]" = type { i32, i32 }
%"main.Point[float32]" = type { float, float }
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.main(ptr %context) unnamed_addr #1 { define hidden void @main.main(i8* %context) unnamed_addr #1 {
entry: entry:
%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) %bi = alloca %"main.Point[int]", align 8
%1 = call %"main.Point[int]" @"main.Add[int]"(i32 0, i32 0, i32 0, i32 0, ptr undef) %ai = alloca %"main.Point[int]", align 8
%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, ptr %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, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %complit = 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 %b = alloca %"main.Point[float32]", align 8
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3 %a = alloca %"main.Point[float32]", align 8
store float %a.X, ptr %a, align 4 %a.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4 store float 0.000000e+00, float* %a.repack, align 8
store float %a.Y, ptr %a.repack9, align 4 %a.repack9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
%b = 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* %a.repack9, align 4
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3 %0 = bitcast %"main.Point[float32]"* %a to i8*
store float %b.X, ptr %b, align 4 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4 %a.repack10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
store float %b.Y, ptr %b.repack11, align 4 store float %a.X, float* %a.repack10, align 8
call void @main.checkSize(i32 4, ptr undef) #3 %a.repack11 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
call void @main.checkSize(i32 8, ptr undef) #3 store float %a.Y, float* %a.repack11, align 4
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %b.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3 store float 0.000000e+00, float* %b.repack, align 8
%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
%0 = load float, ptr %a, align 4 %4 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
%1 = load float, ptr %b, align 4 %5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
%2 = fadd float %0, %1 %6 = load float, float* %5, align 8
%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
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4 %9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4 %10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
%5 = load float, ptr %4, align 4 %11 = load float, float* %10, align 4
%6 = load float, ptr %3, align 4 %12 = load float, float* %9, 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 %2, ptr %complit, align 4 store float %8, float* %3, align 8
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
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4 %13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
%8 = fadd float %5, %6 %14 = fadd float %11, %12
store float %8, ptr %7, align 4 store float %14, float* %13, align 4
%.unpack = load float, ptr %complit, align 4 %.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
%9 = insertvalue %"main.Point[float32]" poison, float %.unpack, 0 %.unpack = load float, float* %.elt, align 8
%10 = insertvalue %"main.Point[float32]" %9, float %8, 1 %15 = insertvalue %"main.Point[float32]" undef, float %.unpack, 0
ret %"main.Point[float32]" %10 %16 = insertvalue %"main.Point[float32]" %15, float %14, 1
ret %"main.Point[float32]" %16
deref.throw: ; preds = %entry deref.throw: ; preds = %entry
unreachable unreachable
@@ -93,64 +159,81 @@ store.throw7: ; preds = %store.next
unreachable unreachable
} }
; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare void @main.checkSize(i32, i8*) #0
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
declare void @main.checkSize(i32, ptr) #0 declare void @runtime.nilPanic(i8*) #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, ptr %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, i8* %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %complit = 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 %b = alloca %"main.Point[int]", align 8
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3 %a = alloca %"main.Point[int]", align 8
store i32 %a.X, ptr %a, align 4 %a.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4 store i32 0, i32* %a.repack, align 8
store i32 %a.Y, ptr %a.repack9, align 4 %a.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 store i32 0, i32* %a.repack9, align 4
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3 %0 = bitcast %"main.Point[int]"* %a to i8*
store i32 %b.X, ptr %b, align 4 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4 %a.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
store i32 %b.Y, ptr %b.repack11, align 4 store i32 %a.X, i32* %a.repack10, align 8
call void @main.checkSize(i32 4, ptr undef) #3 %a.repack11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
call void @main.checkSize(i32 8, ptr undef) #3 store i32 %a.Y, i32* %a.repack11, align 4
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %b.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3 store i32 0, i32* %b.repack, align 8
%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
%0 = load i32, ptr %a, align 4 %4 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
%1 = load i32, ptr %b, align 4 %5 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
%2 = add i32 %0, %1 %6 = load i32, i32* %5, align 8
%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
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4 %9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4 %10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
%5 = load i32, ptr %4, align 4 %11 = load i32, i32* %10, align 4
%6 = load i32, ptr %3, align 4 %12 = load i32, i32* %9, 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 %2, ptr %complit, align 4 store i32 %8, i32* %3, align 8
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
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4 %13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
%8 = add i32 %5, %6 %14 = add i32 %11, %12
store i32 %8, ptr %7, align 4 store i32 %14, i32* %13, align 4
%.unpack = load i32, ptr %complit, align 4 %.elt = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
%9 = insertvalue %"main.Point[int]" poison, i32 %.unpack, 0 %.unpack = load i32, i32* %.elt, align 8
%10 = insertvalue %"main.Point[int]" %9, i32 %8, 1 %15 = insertvalue %"main.Point[int]" undef, i32 %.unpack, 0
ret %"main.Point[int]" %10 %16 = insertvalue %"main.Point[int]" %15, i32 %14, 1
ret %"main.Point[int]" %16
deref.throw: ; preds = %entry deref.throw: ; preds = %entry
unreachable unreachable
@@ -171,7 +254,6 @@ store.throw7: ; preds = %store.next
unreachable unreachable
} }
attributes #0 = { "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,+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 #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
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 #2 = { nounwind }
attributes #3 = { nounwind }
+17 -13
View File
@@ -5,24 +5,27 @@ target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 } %runtime._string = type { ptr, i32 }
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
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 #1 { define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
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 #1 { define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = icmp slt i16 %len, 0 %0 = icmp slt i16 %len, 0
@@ -36,24 +39,25 @@ 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) #2 call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
ret %runtime._string %7 ret %runtime._string %7
unsafe.String.throw: ; preds = %entry unsafe.String.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #2 call void @runtime.unsafeSlicePanic(ptr undef) #3
unreachable unreachable
} }
declare void @runtime.unsafeSlicePanic(ptr) #0 declare void @runtime.unsafeSlicePanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 { define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
ret ptr %s.data ret ptr %s.data
} }
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nounwind } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind }
+43 -39
View File
@@ -5,32 +5,35 @@ target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 } %runtime._string = type { ptr, i32 }
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #1 { define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
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) #2 declare i32 @llvm.smin.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
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)
@@ -38,7 +41,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 #1 { define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
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)
@@ -47,109 +50,109 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #1 { define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
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) #2 declare i8 @llvm.umin.i8(i8, i8) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
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) #2 declare i32 @llvm.umin.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #1 { define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
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) #2 declare float @llvm.minimum.f32(float, float) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #1 { define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
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) #2 declare double @llvm.minimum.f64(double, double) #3
; 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 #1 { 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 {
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) #4 %4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
%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) #4 call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._string %5 ret %runtime._string %5
} }
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0 declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
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) #2 declare i32 @llvm.smax.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
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) #2 declare i32 @llvm.umax.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #1 { define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
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) #2 declare float @llvm.maximum.f32(float, float) #3
; 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 #1 { 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 {
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) #4 %4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
%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) #4 call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
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 #1 { define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
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)
@@ -157,25 +160,26 @@ 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) #3 declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #4
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 { define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.clearMap(ptr dereferenceable_or_null(48) %m, ptr %context) unnamed_addr #1 { define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
entry: entry:
call void @runtime.hashmapClear(ptr %m, ptr undef) #4 call void @runtime.hashmapClear(ptr %m, ptr undef) #5
ret void ret void
} }
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(48), ptr) #0 declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #4 = { nounwind } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #5 = { nounwind }
+28 -28
View File
@@ -5,36 +5,39 @@ 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 #0 { 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 #0 { define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
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) #1 declare void @main.regularFunction(i32, ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 { define linkonce_odr void @"main.regularFunction$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.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) #1 declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #2
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1 declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #0 { define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
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
@@ -42,13 +45,13 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 { 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 #3 { define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
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)
@@ -56,7 +59,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 { define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
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
@@ -73,11 +76,8 @@ 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 #0 { 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
@@ -93,14 +93,14 @@ entry:
ret void ret void
} }
declare void @runtime.printlock(ptr) #1 declare void @runtime.printlock(ptr) #2
declare void @runtime.printint32(i32, ptr) #1 declare void @runtime.printint32(i32, ptr) #2
declare void @runtime.printunlock(ptr) #1 declare void @runtime.printunlock(ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #0 { define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
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 #0 { 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 #0 { 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)
@@ -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 #0 { 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) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #0 { define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
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 = { 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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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
View File
@@ -5,27 +5,30 @@ 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
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 #2 {
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) #0 declare void @main.regularFunction(i32, ptr) #1
declare void @runtime.deadlock(ptr) #0 declare void @runtime.deadlock(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 { define linkonce_odr void @"main.regularFunction$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.regularFunction(i32 %unpack.int, ptr undef) #11 call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
@@ -33,23 +36,23 @@ entry:
unreachable unreachable
} }
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0 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 #2 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 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 #1 { define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 { define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
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)
@@ -58,7 +61,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 #2 {
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
@@ -79,11 +82,8 @@ 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 #2 {
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) #0 declare void @runtime.printlock(ptr) #1
declare void @runtime.printint32(i32, ptr) #0 declare void @runtime.printint32(i32, ptr) #1
declare void @runtime.printunlock(ptr) #0 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 #2 {
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 #1 { define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #2 {
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 #2 {
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 #1 { define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
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) #0 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 #2 {
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 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
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 #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 #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 #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 #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) }
+41 -37
View File
@@ -19,54 +19,57 @@ 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
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) #6 call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
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 #1 { define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
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) #6 call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
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 #1 { define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
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) #6 call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
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 #1 { define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
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) #6 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 null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
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 #1 { define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry: entry:
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6 %typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
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
@@ -76,12 +79,12 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next br label %typeassert.next
} }
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0 declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6 %0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
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
@@ -91,12 +94,12 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next br label %typeassert.next
} }
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2 declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6 %0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
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
@@ -106,33 +109,34 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next br label %typeassert.next
} }
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3 declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
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) #6 %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
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) #4 declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
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) #6 %0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
%1 = extractvalue %runtime._string %0, 0 %1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
ret %runtime._string %0 ret %runtime._string %0
} }
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5 declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #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 #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
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 #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 #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 #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 #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 #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 #6 = { nounwind } 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 #7 = { nounwind }
+16 -12
View File
@@ -3,44 +3,48 @@ 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"
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
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 #1 { define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #2 {
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 #1 { define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
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 #1 { define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
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 #1 { define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
ret ptr %x ret ptr %x
} }
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nounwind } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind }
-5
View File
@@ -115,8 +115,3 @@ 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)
}
+30 -40
View File
@@ -11,105 +11,95 @@ 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define void @extern_func() #2 { define void @extern_func() #3 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 { define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
declare void @somepkg.someFunction2(ptr) #0 declare void @somepkg.someFunction2(ptr) #1
; Function Attrs: inlinehint nounwind ; Function Attrs: inlinehint nounwind
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 { define hidden void @main.inlineFunc(ptr %context) unnamed_addr #4 {
entry: entry:
ret void ret void
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 { define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #5 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.useGeneric(ptr %context) unnamed_addr #1 { define hidden void @main.useGeneric(ptr %context) unnamed_addr #2 {
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 #4 { define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #5 {
entry: entry:
ret void ret void
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.functionInSection(ptr %context) unnamed_addr #4 section ".special_function_section" { define hidden void @main.functionInSection(ptr %context) unnamed_addr #5 section ".special_function_section" {
entry: entry:
ret void ret void
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define void @exportedFunctionInSection() #5 section ".special_function_section" { define void @exportedFunctionInSection() #6 section ".special_function_section" {
entry: entry:
ret void ret void
} }
declare void @main.declaredImport() #6 declare void @main.declaredImport() #7
declare void @imported() #7 declare void @imported() #8
; Function Attrs: nounwind ; Function Attrs: nounwind
define void @exported() #8 { define void @exported() #9 {
entry: entry:
ret void ret void
} }
declare void @main.undefinedFunctionNotInSection(ptr) #0 declare void @main.undefinedFunctionNotInSection(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 declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #1
; 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 #1 { 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 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind 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" }
define hidden ptr @main.doesHeapAlloc(ptr %context) unnamed_addr #1 { attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
entry: attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
%stackalloc = alloca i8, align 1 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" }
%new = call align 4 dereferenceable(4) ptr @runtime.alloc_noheap(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #10 attributes #4 = { inlinehint 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 #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
ret ptr %new 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" }
} 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" }
; Function Attrs: allockind("alloc,zeroed") allocsize(0) 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" }
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 }
+31 -31
View File
@@ -3,28 +3,31 @@ 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"
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #2 {
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
@@ -39,10 +42,10 @@ lookup.throw: ; preds = %entry
unreachable unreachable
} }
declare void @runtime.lookupPanic(ptr) #0 declare void @runtime.lookupPanic(ptr) #1
; 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 #1 { define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
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
@@ -63,13 +66,10 @@ entry:
ret { ptr, i32, i32 } %4 ret { ptr, i32, i32 } %4
} }
; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1
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 #1 { 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 {
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 #1 { 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 {
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 #1 { define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 {
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) #0 declare void @runtime.slicePanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 { define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
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) #0 declare void @runtime.sliceToArrayPointerPanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 { define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
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 #1 { define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #2 {
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) #0 declare void @runtime.unsafeSlicePanic(ptr) #1
; 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 #1 { define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
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 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #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 #2 = { 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 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 }
+25 -21
View File
@@ -7,34 +7,37 @@ 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #1 { define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #2 {
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 #1 { define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #2 {
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 #1 { define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
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 #1 { define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
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
@@ -45,40 +48,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) #2 call void @runtime.lookupPanic(ptr undef) #3
unreachable unreachable
} }
declare void @runtime.lookupPanic(ptr) #0 declare void @runtime.lookupPanic(ptr) #1
; 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 #1 { define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2 %0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0 declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
; 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 #1 { define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2 %0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
%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 #1 { define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #2 %0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0 declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #1 { define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
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
@@ -90,10 +93,11 @@ lookup.next: ; preds = %entry
ret i8 %2 ret i8 %2
lookup.throw: ; preds = %entry lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(ptr undef) #2 call void @runtime.lookupPanic(ptr undef) #3
unreachable unreachable
} }
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { nounwind } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind }
+54 -24
View File
@@ -5,16 +5,19 @@ target triple = "wasm32-unknown-wasi"
%main.hasPadding = type { i1, i32, i1 } %main.hasPadding = type { i1, i32, i1 }
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: allockind("alloc,zeroed") allocsize(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 #1 { define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
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 { 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 {
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
@@ -24,23 +27,29 @@ 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 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4 %3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
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)
%4 = load i32, ptr %hashmap.value, align 4 %6 = 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 %4 ret i32 %6
} }
; 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) #4
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0 declare void @runtime.memzero(ptr, i32, ptr) #1
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) #3 declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 { define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
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
@@ -51,16 +60,20 @@ 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
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4 %3 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
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.hashmapGenericSet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, ptr) #0 declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #1
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 { define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
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
@@ -71,15 +84,23 @@ 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 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4 %0 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
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)
%1 = load i32, ptr %hashmap.value, align 4 %5 = 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 %1 ret i32 %5
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 { define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
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
@@ -91,20 +112,29 @@ 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
call void @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4 %0 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 1
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 #1 { define hidden void @main.main(ptr %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } 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 #1 = { nounwind "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 #2 = { noinline 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 #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #4 = { nounwind } attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #5 = { nounwind }
-1
View File
@@ -38,7 +38,6 @@ 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"},
+1 -2
View File
@@ -19,7 +19,6 @@ 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
@@ -30,7 +29,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 == errLoopTooLong || err == errInvalidPtrToIntSize err == errLoopUnrolled || 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.
+30 -33
View File
@@ -19,38 +19,35 @@ 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
maxLoopIterations int callsExecuted uint64
callsExecuted uint64
interpErr error // set by Uint/Int when they encounter pointer data
} }
func newRunner(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bool) *runner { func newRunner(mod llvm.Module, timeout time.Duration, 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)
@@ -67,8 +64,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, maxLoopIterations int, debug bool) error { func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
r := newRunner(mod, timeout, maxLoopIterations, debug) r := newRunner(mod, timeout, debug)
defer r.dispose() defer r.dispose()
initAll := mod.NamedFunction("runtime.initAll") initAll := mod.NamedFunction("runtime.initAll")
@@ -207,10 +204,10 @@ func Run(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bo
// 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, maxLoopIterations int, debug bool) error { func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
// Create and initialize *runner object. // Create and initialize *runner object.
mod := fn.GlobalParent() mod := fn.GlobalParent()
r := newRunner(mod, timeout, maxLoopIterations, debug) r := newRunner(mod, timeout, debug)
defer r.dispose() defer r.dispose()
initName := fn.Name() initName := fn.Name()
if !strings.HasSuffix(initName, ".init") { if !strings.HasSuffix(initName, ".init") {
+1 -1
View File
@@ -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, DefaultMaxInterpBlockEntries, false) err = Run(mod, 10*time.Minute, 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())
+1 -31
View File
@@ -12,12 +12,6 @@ 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))
@@ -32,10 +26,6 @@ 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]
@@ -46,18 +36,6 @@ 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.
@@ -299,7 +277,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" || callFn.name == "runtime.alloc_noheap": case callFn.name == "runtime.alloc":
// 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.
@@ -847,14 +825,6 @@ 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"))
} }
+5 -11
View File
@@ -556,13 +556,11 @@ func (v pointerValue) asRawValue(r *runner) rawValue {
} }
func (v pointerValue) Uint(r *runner) uint64 { func (v pointerValue) Uint(r *runner) uint64 {
r.interpErr = errUnsupportedInst panic("cannot convert pointer to integer")
return 0
} }
func (v pointerValue) Int(r *runner) int64 { func (v pointerValue) Int(r *runner) int64 {
r.interpErr = errUnsupportedInst panic("cannot convert pointer to integer")
return 0
} }
func (v pointerValue) equal(rhs pointerValue) bool { func (v pointerValue) equal(rhs pointerValue) bool {
@@ -738,12 +736,11 @@ func (v rawValue) asRawValue(r *runner) rawValue {
return v return v
} }
func (v rawValue) bytes(r *runner) []byte { func (v rawValue) bytes() []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 {
r.interpErr = errUnsupportedInst panic("cannot convert pointer value to byte")
return buf
} }
buf[i] = byte(p) buf[i] = byte(p)
} }
@@ -751,10 +748,7 @@ func (v rawValue) bytes(r *runner) []byte {
} }
func (v rawValue) Uint(r *runner) uint64 { func (v rawValue) Uint(r *runner) uint64 {
buf := v.bytes(r) buf := v.bytes()
if r.interpErr != nil {
return 0
}
switch len(v.buf) { switch len(v.buf) {
case 1: case 1:
+1 -1
View File
@@ -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,
-23
View File
@@ -304,29 +304,6 @@ 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 {
+1 -3
View File
@@ -8,9 +8,7 @@ import (
// //
// The program must already be parsed and type-checked with the .Parse() method. // The program must already be parsed and type-checked with the .Parse() method.
func (p *Program) LoadSSA() *ssa.Program { func (p *Program) LoadSSA() *ssa.Program {
// TODO: re-enable SanityCheckFunctions when x/tools is upgraded to prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
// a version with a fix for https://golang.org/issues/73594.
prog := ssa.NewProgram(p.fset /*ssa.SanityCheckFunctions|*/, ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
for _, pkg := range p.sorted { for _, pkg := range p.sorted {
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true) prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
+37 -40
View File
@@ -30,7 +30,6 @@ 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"
@@ -589,7 +588,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, port, config) return Monitor(result.Executable, "", config)
} }
return nil return nil
} }
@@ -1739,7 +1738,6 @@ 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")
@@ -1857,43 +1855,42 @@ 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,
InterpMaxLoopIterations: *interpLoopLimit, PrintIR: *printIR,
PrintIR: *printIR, DumpSSA: *dumpSSA,
DumpSSA: *dumpSSA, VerifyIR: *verifyIR,
VerifyIR: *verifyIR, SkipDWARF: *skipDwarf,
SkipDWARF: *skipDwarf, Semaphore: make(chan struct{}, *parallelism),
Semaphore: make(chan struct{}, *parallelism), Debug: !*nodebug,
Debug: !*nodebug, Nobounds: *nobounds,
Nobounds: *nobounds, PrintSizes: *printSize,
PrintSizes: *printSize, PrintStacks: *printStacks,
PrintStacks: *printStacks, PrintAllocs: printAllocs,
PrintAllocs: printAllocs, Tags: []string(tags),
Tags: []string(tags), TestConfig: testConfig,
TestConfig: testConfig, GlobalValues: globalVarValues,
GlobalValues: globalVarValues, Programmer: *programmer,
Programmer: *programmer, OpenOCDCommands: ocdCommands,
OpenOCDCommands: ocdCommands, LLVMFeatures: *llvmFeatures,
LLVMFeatures: *llvmFeatures, Monitor: *monitor,
Monitor: *monitor, BaudRate: *baudrate,
BaudRate: *baudrate, Timeout: *timeout,
Timeout: *timeout, WITPackage: witPackage,
WITPackage: witPackage, WITWorld: witWorld,
WITWorld: witWorld, GoCompatibility: *gocompatibility,
GoCompatibility: *gocompatibility,
} }
if *printCommands { if *printCommands {
options.PrintCommands = printCommand options.PrintCommands = printCommand
-19
View File
@@ -77,7 +77,6 @@ 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",
@@ -156,19 +155,6 @@ 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() {
@@ -297,11 +283,6 @@ 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
+8 -2
View File
@@ -453,7 +453,10 @@ 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 = unsafe.String((*byte)(data), tagLen) tag = *(*string)(unsafe.Pointer(&stringHeader{
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
@@ -927,7 +930,10 @@ func readStringZ(data unsafe.Pointer) string {
data = unsafe.Add(data, 1) // C: data++ data = unsafe.Add(data, 1) // C: data++
} }
return unsafe.String((*byte)(start), len) return *(*string)(unsafe.Pointer(&stringHeader{
data: start,
len: len,
}))
} }
func (t *RawType) name() string { func (t *RawType) name() string {
+88 -98
View File
@@ -146,19 +146,6 @@ 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
@@ -171,7 +158,11 @@ 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.
v.value = loadSmallValue(v.value, v.typecode.Size()) var value uintptr
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)
} }
@@ -722,12 +713,20 @@ 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 := *(*string)(v.value) str := *(*stringHeader)(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(&sliced), value: unsafe.Pointer(&hdr),
flags: v.flags, flags: v.flags,
} }
} }
@@ -802,7 +801,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 len(*(*string)(v.value)) return int((*stringHeader)(v.value).len)
default: default:
panic(&ValueError{Method: "Len", Kind: v.Kind()}) panic(&ValueError{Method: "Len", Kind: v.Kind()})
} }
@@ -978,10 +977,13 @@ 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 := *(*string)(v.value) s := *(*stringHeader)(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(s[i])), value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Add(s.data, i)))),
flags: v.flags & valueFlagExported, flags: v.flags & valueFlagExported,
} }
case Array: case Array:
@@ -1083,8 +1085,18 @@ 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) {
keys = append(keys, k.Elem()) if shouldUnpackInterface {
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())
} }
@@ -1097,40 +1109,9 @@ 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 hashmapGenericGet runtime.hashmapGenericGet //go:linkname hashmapInterfaceGet runtime.hashmapInterfaceGet
func hashmapGenericGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool func hashmapInterfaceGet(m unsafe.Pointer, key interface{}, 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()})
@@ -1159,16 +1140,13 @@ 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 {
// Compiler-generated hash/equal path: keys are stored at their if ok := hashmapInterfaceGet(v.pointer(), key.Interface(), elem.value, elemType.Size()); !ok {
// 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()
@@ -1193,7 +1171,8 @@ 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 {
@@ -1201,6 +1180,12 @@ 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()
} }
@@ -1233,9 +1218,15 @@ 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,
} }
} }
@@ -1723,7 +1714,8 @@ 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"
zerobuffer = unsafe.Pointer(unsafe.StringData(zerobufferStr)) s := (*stringHeader)(unsafe.Pointer(&zerobufferStr))
zerobuffer = s.data
} }
func Zero(typ Type) Value { func Zero(typ Type) Value {
@@ -1774,11 +1766,19 @@ type sliceHeader struct {
cap uintptr cap uintptr
} }
// Verify SliceHeader size. // Like sliceHeader, this type is used internally to make sure pointer and
// 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 length uintptr var len 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
length = hdr.len len = 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)
} }
length = uintptr(v.Len()) len = uintptr(v.Len())
case String: case String:
s := *(*string)(v.value) hdr := (*stringHeader)(v.value)
buf = unsafe.Pointer(unsafe.StringData(s)) buf = hdr.data
length = uintptr(len(s)) len = hdr.len
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, length return buf, len
} }
//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 hashmapGenericSet runtime.hashmapGenericSet //go:linkname hashmapInterfaceSet runtime.hashmapInterfaceSet
func hashmapGenericSet(m unsafe.Pointer, key, value unsafe.Pointer) func hashmapInterfaceSet(m unsafe.Pointer, key interface{}, 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 hashmapGenericDelete runtime.hashmapGenericDelete //go:linkname hashmapInterfaceDelete runtime.hashmapInterfaceDelete
func hashmapGenericDelete(m unsafe.Pointer, key unsafe.Pointer) func hashmapInterfaceDelete(m unsafe.Pointer, key interface{})
func (v Value) SetMapIndex(key, elem Value) { func (v Value) SetMapIndex(key, elem Value) {
v.checkRO() v.checkRO()
@@ -2002,19 +2002,15 @@ 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 !del && v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface { if v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
val := elem.value intf := composeInterface(unsafe.Pointer(elem.typecode), 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 vkey.Kind() == String { if key.Kind() == String {
if del { if del {
hashmapStringDelete(v.pointer(), *(*string)(key.value)) hashmapStringDelete(v.pointer(), *(*string)(key.value))
} else { } else {
@@ -2027,7 +2023,7 @@ func (v Value) SetMapIndex(key, elem Value) {
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr) hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
} }
} else if vkey.isBinary() { } else if key.typecode.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
@@ -2047,11 +2043,8 @@ 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 {
hashmapGenericDelete(v.pointer(), keyptr) hashmapInterfaceDelete(v.pointer(), key.Interface())
} 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)) {
@@ -2060,7 +2053,7 @@ func (v Value) SetMapIndex(key, elem Value) {
elemptr = unsafe.Pointer(&elem.value) elemptr = unsafe.Pointer(&elem.value)
} }
hashmapGenericSet(v.pointer(), keyptr, elemptr) hashmapInterfaceSet(v.pointer(), key.Interface(), elemptr)
} }
} }
} }
@@ -2117,9 +2110,6 @@ 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 {
@@ -2128,6 +2118,7 @@ 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 {
@@ -2141,19 +2132,18 @@ func MakeMapWithSize(typ Type, n int) Value {
key := typ.Key().(*RawType) key := typ.Key().(*RawType)
val := typ.Elem().(*RawType) val := typ.Elem().(*RawType)
var m unsafe.Pointer var alg uint8
if key.Kind() == String { if key.Kind() == String {
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmString) alg = hashmapAlgorithmString
} else if key.isBinary() { } else if key.isBinary() {
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmBinary) alg = hashmapAlgorithmBinary
} else { } else {
// Composite key type (struct with strings, floats, etc.). alg = hashmapAlgorithmInterface
// 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,
-99
View File
@@ -1,99 +0,0 @@
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
View File
@@ -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
taskWaiter task.Waiter // Waiter for events outside interrupt context taskQueued bool // Flag to indicate if the buffer has a task queued
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 {
// Wait for the next task to arrive. if m.taskQueued {
m.taskWaiter.Wait() cmd := m.cbw.SCSICmd()
switch cmd.CmdType() {
case scsi.CmdWrite:
m.scsiWrite(cmd, m.buf)
case scsi.CmdUnmap:
m.scsiUnmap(m.buf)
}
cmd := m.cbw.SCSICmd() // Acknowledge the received data from the host
switch cmd.CmdType() { m.queuedBytes = 0
case scsi.CmdWrite: m.taskQueued = false
m.scsiWrite(cmd, m.buf) machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
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)
} }
} }
+4 -4
View File
@@ -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.taskWaiter.Working() { if m.taskQueued {
// 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.taskWaiter.Resume() m.taskQueued = true
} }
case scsi.CmdUnmap: case scsi.CmdUnmap:
m.taskWaiter.Resume() m.taskQueued = true
} }
// Don't acknowledge the incoming data until we can process it. // Don't acknowledge the incoming data until we can process it.
return !m.taskWaiter.Working() return !m.taskQueued
} }
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: 1026408a38...e54965ed5e
+68 -247
View File
@@ -13,27 +13,16 @@ 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 keySize uintptr // maybe this can store the key type as well? E.g. keysize == 5 means string?
valueSize uintptr valueSize uintptr
keySlotSize uintptr // == keySize, or sizeof(ptr) if indirect bucketBits uint8
valueSlotSize uintptr // == valueSize, or sizeof(ptr) if indirect keyEqual func(x, y unsafe.Pointer, n uintptr) bool
bucketBits uint8 keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
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
@@ -45,48 +34,6 @@ 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
@@ -119,35 +66,20 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm
bucketBits++ bucketBits++
} }
var flags uint8 bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + keySize*8 + valueSize*8
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,
keySlotSize: keySlotSize, bucketBits: bucketBits,
valueSlotSize: valueSlotSize, keyEqual: keyEqual,
bucketBits: bucketBits, keyHash: keyHash,
flags: flags,
keyEqual: keyEqual,
keyHash: keyHash,
} }
} }
@@ -186,6 +118,8 @@ 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
@@ -198,6 +132,8 @@ 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
@@ -236,7 +172,7 @@ func hashmapLen(m *hashmap) int {
//go:inline //go:inline
func hashmapBucketSize(m *hashmap) uintptr { func hashmapBucketSize(m *hashmap) uintptr {
return hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*8 return unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
} }
//go:inline //go:inline
@@ -255,14 +191,16 @@ 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 := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*uintptr(slot) slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(slot)
return unsafe.Add(unsafe.Pointer(bucket), slotKeyOffset) slotKey := 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 := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*uintptr(slot) slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(slot)
return unsafe.Add(unsafe.Pointer(bucket), slotValueOffset) slotValue := 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.
@@ -296,9 +234,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, hashmapSlotKeyData(m, slotKey), m.keySize) { if m.keyEqual(key, slotKey, m.keySize) {
// found same key, replace the value // found same key, replace it
hashmapStoreValue(m, slotValue, value) memcpy(slotValue, value, m.valueSize)
return return
} }
} }
@@ -313,45 +251,11 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
return return
} }
m.count++ m.count++
hashmapStoreKey(m, emptySlotKey, key) memcpy(emptySlotKey, key, m.keySize)
hashmapStoreValue(m, emptySlotValue, value) memcpy(emptySlotValue, value, m.valueSize)
*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 {
@@ -363,8 +267,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++
hashmapStoreKey(m, slotKey, key) memcpy(slotKey, key, m.keySize)
hashmapStoreValue(m, slotValue, value) memcpy(slotValue, value, m.valueSize)
bucket.tophash[0] = tophash bucket.tophash[0] = tophash
return bucket return bucket
} }
@@ -427,12 +331,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, hashmapSlotKeyData(m, slotKey), m.keySize) { if m.keyEqual(key, slotKey, m.keySize) {
// Found the key, copy it. // Found the key, copy it.
slotValue := hashmapSlotValue(m, bucket, i) memcpy(value, slotValue, m.valueSize)
memcpy(value, hashmapSlotValueData(m, slotValue), m.valueSize)
return true return true
} }
} }
@@ -466,15 +370,13 @@ 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, hashmapSlotKeyData(m, slotKey), m.keySize) { if m.keyEqual(key, slotKey, m.keySize) {
// Found the key, delete it. // Found the key, delete it.
bucket.tophash[i] = 0 bucket.tophash[i] = 0
// Zero out the slot so the GC won't pin the allocations. // Zero out the key and value so garbage collector doesn't pin the allocations.
keySlotSize := hashmapKeySlotSize(m) memzero(slotKey, m.keySize)
memzero(slotKey, keySlotSize)
slotValue := hashmapSlotValue(m, bucket, i) slotValue := hashmapSlotValue(m, bucket, i)
valueSlotSize := hashmapValueSlotSize(m) memzero(slotValue, m.valueSize)
memzero(slotValue, valueSlotSize)
m.count-- m.count--
return return
} }
@@ -536,13 +438,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, hashmapSlotKeyData(m, slotKey), m.keySize) memcpy(key, 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, hashmapSlotValueData(m, slotValue), m.valueSize) memcpy(value, slotValue, m.valueSize)
it.bucketIndex++ it.bucketIndex++
} else { } else {
it.bucketIndex++ it.bucketIndex++
@@ -589,112 +491,6 @@ 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 {
@@ -830,3 +626,28 @@ 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)
}
-5
View File
@@ -66,11 +66,6 @@ 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
-31
View File
@@ -82,34 +82,3 @@ 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
}
+1 -1
View File
@@ -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 unsafe.Slice((*byte)(addr), length), nil return (*[1 << 30]byte)(addr)[:length:length], nil
} }
func Munmap(b []byte) (err error) { func Munmap(b []byte) (err error) {
+1
View File
@@ -13,4 +13,5 @@ type HashmapAlgorithm uint8
const ( const (
HashmapAlgorithmBinary HashmapAlgorithm = iota HashmapAlgorithmBinary HashmapAlgorithm = iota
HashmapAlgorithmString HashmapAlgorithmString
HashmapAlgorithmInterface
) )
-39
View File
@@ -243,9 +243,6 @@
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++) {
@@ -287,8 +284,6 @@
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;
}, },
@@ -299,13 +294,6 @@
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.
@@ -340,15 +328,12 @@
// 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);
@@ -357,8 +342,6 @@
// 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);
@@ -367,8 +350,6 @@
// 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);
@@ -386,11 +367,6 @@
// 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);
@@ -406,9 +382,6 @@
// 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);
@@ -422,9 +395,6 @@
// 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 {
@@ -443,7 +413,6 @@
// 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);
@@ -452,8 +421,6 @@
// 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);
}, },
@@ -465,9 +432,6 @@
// 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
@@ -487,9 +451,6 @@
// 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
-69
View File
@@ -1,69 +0,0 @@
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
-9
View File
@@ -1,9 +0,0 @@
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)
}
-1
View File
@@ -1 +0,0 @@
someGlobal: foobar
+2 -1
View File
@@ -1,6 +1,7 @@
package main package main
// This global can be changed using -ldflags="-X main.someGlobal=value". // These globals 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() {
-85
View File
@@ -29,14 +29,6 @@ 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
@@ -138,10 +130,6 @@ func main() {
mapgrow() mapgrow()
nestedarraymaps()
ptrmaps()
interfacerehash() interfacerehash()
} }
@@ -320,76 +308,3 @@ 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
}
-11
View File
@@ -80,15 +80,4 @@ 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
-63
View File
@@ -1,63 +0,0 @@
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)
}
-8
View File
@@ -1,8 +0,0 @@
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
-140
View File
@@ -396,24 +396,6 @@ 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() {
@@ -773,9 +755,6 @@ 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 {
@@ -828,122 +807,3 @@ 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")
}
}
-19
View File
@@ -503,24 +503,6 @@ 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
@@ -530,4 +512,3 @@ blue gopher
v.Interface() method v.Interface() method
kind: interface kind: interface
int 5 int 5
reflect map interface key ok
-98
View File
@@ -1,98 +0,0 @@
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
}
}
+3 -16
View File
@@ -23,15 +23,8 @@ 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)) {
// Find allocator functions. allocator := mod.NamedFunction("runtime.alloc")
var allocators []llvm.Value if allocator.IsNil() {
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
} }
@@ -50,13 +43,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
fmt.Fprintln(os.Stderr, "mode: set") fmt.Fprintln(os.Stderr, "mode: set")
} }
// Find all allocator calls. for _, heapalloc := range getUses(allocator) {
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.
+24 -30
View File
@@ -10,44 +10,38 @@ 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")
hashmapMakeGeneric := mod.NamedFunction("runtime.hashmapMakeGeneric") if hashmapMake.IsNil() {
// 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")
optimizeMapMake := func(makeFunc llvm.Value) { for _, makeInst := range getUses(hashmapMake) {
if makeFunc.IsNil() { updateInsts := []llvm.Value{}
return unknownUses := false // are there any uses other than setting a value?
}
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, hashmapGenericSet: case hashmapBinarySet, hashmapStringSet:
updateInsts = append(updateInsts, use) updateInsts = append(updateInsts, use)
default: default:
unknownUses = true
}
} else {
unknownUses = true unknownUses = true
} }
} } else {
unknownUses = true
if !unknownUses {
// This map can be entirely removed, as it is only created
// but never used.
for _, inst := range updateInsts {
inst.EraseFromParentAsInstruction()
}
makeInst.EraseFromParentAsInstruction()
} }
} }
}
optimizeMapMake(hashmapMake) if !unknownUses {
optimizeMapMake(hashmapMakeGeneric) // This map can be entirely removed, as it is only created but never
// used.
for _, inst := range updateInsts {
inst.EraseFromParentAsInstruction()
}
makeInst.EraseFromParentAsInstruction()
}
}
} }