Compare commits

..

18 Commits

Author SHA1 Message Date
Ayke van Laethem 88db2c3594 usb/msc: wait for interrupt instead of polling a flag
This should make usb/msc a whole lot more efficient by pausing the
worker goroutine and waiting for an interrupt to unpause it instead of
waiting in a loop and sleeping for 0.1ms each cycle.

In other words, this should make it both faster (no unnecessary delay
due to the time.Sleep) and more efficient (no polling).
2026-05-25 17:08:01 +02:00
Ayke van Laethem fb2755d18f internal/task: add Waiter for waiting for a flag from interrupts
This provides an abstraction to allow goroutines to wait for an event
from an interrupt, and for interrupts to send such an event and know
whether the goroutine is still working on the previous event.
2026-05-25 17:06:23 +02:00
Ayke van Laethem 85d223c5e2 compiler: add //go:noheap pragma 2026-05-25 16:58:01 +02:00
Ayke van Laethem 7b486e398a compiler: clean up pointer pack code
This means runtime.alloc isn't allocated every time but instead only
when it is actually needed. It also just cleans up the compiler code a
little bit, and makes the next commit a bit nicer.
2026-05-25 16:58:01 +02:00
felipegenef da69f18e91 fix targets/wasm_exec.js: move unsigned coercions from helpers to top-level import functions 2026-05-21 14:39:17 -07:00
felipegenef 6971b8a69e fix: coerce memory offsets to unsigned before typed array and DataView access 2026-05-21 14:39:17 -07:00
Ayke van Laethem 6682e41ab4 reflectlite: remove stringHeader
It's usually a better idea to use unsafe.String and such instead.
2026-05-20 14:33:41 +02:00
Jake Bailey 18033ebc36 compiler, runtime, reflect: generate type-specific hash/equal (#5359)
* compiler, runtime, reflect: generate type-specific hash/equal for composite map keys

For map keys that are not trivially binary-comparable, the compiler now
generates type-specific hash and equal functions as LLVM IR instead of
going through the interface+reflection path. This covers comparable
types: strings, floats, complex numbers, interfaces, channels, and
composites containing any mix of these.

Previously, maps with composite keys containing strings or floats
converted the key to interface{}, hashed via reflection, and compared
through interface equality. Now the compiler walks struct fields and
array elements directly, dispatching to the right runtime helper for
each field type and storing keys at their actual type.

Struct keys are always handled field-by-field so padding bytes do not
affect equality or hashing. Blank fields are ignored, matching Go
equality. Generated hash/equal function names use canonical underlying
type structure so structurally identical key types can share generated
functions. Padding zeroing before map operations is no longer needed
because structs no longer use the binary key path.

Also fix reflect map iteration for interface-keyed maps: MapIter.Key
returns an interface Value for map[interface{}] keys instead of
unpacking to the concrete key kind.

* compiler: generate loops for array map key hash/equal

Previously, array key hash and equal functions were unrolled at compile
time, generating one block of IR per element. For large arrays like
[1000]int inside a struct with non-binary fields, this caused code
explosion.

Now, binary-element arrays dispatch directly to hash32/memequal for the
whole array. Non-binary-element arrays generate an LLVM IR loop. The
equal loop short-circuits on the first mismatch.

Small arrays are still unrolled instead of looping, keeping the simple
cases compact.

* reflect: fix at-runtime map issues from review, and more found locally

Maps created through reflect.MakeMap need hash/equal behavior that
matches compiler-created maps. Add hashmapMakeReflect for composite key
types, using runtime closures that reconstruct interface{} values from
raw key bytes and delegate to the interface hash and equality paths.

Interface-keyed maps are already stored as interface values, so use the
existing interface hash/equal helpers directly for those. This keeps
reflect insert, lookup, delete, and compiled lookup paths consistent.

Also fix addressable small values used as interface map keys or
interface map values. loadSmallValue puts small indirect values back in
the pointer-sized interface data field the same way valueInterfaceUnsafe
does.

* compiler, interp, reflect: fix pointer map literals; remove interface fallback

Package-level map literals with pointer keys (both *T and
unsafe.Pointer) crash the compiler: the interp pass panics when trying
to hash pointer data as raw bytes, because pointer values in the interp
memory model are symbolic identities that do not fit in a byte.

Fix this by setting a recoverable error flag instead of panicking. The
interp detects the error after each instruction and defers the map
insert to runtime init code, where real addresses are available for
hashing. This matches how the interp already handles other operations
it cannot evaluate at compile time.

With this fix, unsafe.Pointer can also be classified as a binary map
key, which was the last type requiring the interface-based fallback.
Since all comparable types now use either the binary or the
compiler-generated hash/equal path, remove the interface fallback from
the compiler and reflect packages.

* compiler, transform: always pass hash/equal function pointers to hashmapMakeGeneric

The compiler now always resolves the hash and equal functions at compile
time and passes them directly to hashmapMakeGeneric, instead of passing
an algorithm enum to hashmapMake and resolving at runtime. For string
keys, the runtime hashmapStringPtrHash/hashmapStringEqual functions are
referenced directly. For binary keys, hash32/memequal are referenced.

The old hashmapMake with alg enum is retained for reflect, which still
needs runtime resolution when creating maps dynamically.

The OptimizeMaps transform pass is updated to handle both hashmapMake
and hashmapMakeGeneric, and to recognize hashmapGenericSet in addition
to hashmapBinarySet and hashmapStringSet. The now-unused
hashmapCanGenerateHashEqual helper is removed.

* runtime: store large map keys and values indirectly

When a map key or value exceeds 128 bytes, the bucket now stores a
pointer to separately allocated memory instead of the data inline. This
matches Go's MapMaxKeyBytes/MapMaxElemBytes threshold and prevents
bucket sizes from exploding for large key/value types.

For example, map[[256]byte]int previously used 2128 bytes per bucket
(16 header + 256*8 keys + 8*8 values); now it uses 144 bytes per bucket
(16 header + 8*8 pointers + 8*8 values).

The indirection is fully encapsulated in the runtime via helper
functions. Store the computed key and value slot sizes on the hashmap so
all runtime and reflect paths use the same bucket layout, including
non-indirect keys and values.

Add big-key golden coverage and benchmarks. Make the benchmark vary
enough key bytes to exercise hashing.
2026-05-18 13:31:27 +02:00
Jake Bailey 89d9e33bca interp: bail out of loops that iterate too many times (#5395)
* interp: bail out of loops that iterate too many times

The existing loop guard (errLoopUnrolled) only fires when a loop body
emits runtime instructions. Loops that are fully evaluable at compile
time, such as inserting thousands of entries into a map, were not
caught and could hang the compiler.

Add a per-basic-block iteration counter that triggers a recoverable
error (errLoopTooLong) when any block is entered more than 1000 times
in a single function call. This defers the init function to runtime,
which is the same behavior as other interp bailouts.

Profiling showed that 83% of CPU time was spent in GC, caused by
allocation pressure from the interp memory cloning on each map
mutation. The iteration limit avoids this entirely by bailing out
before the quadratic cost becomes significant.

Performance on the reproducer from #2090 (map init with strconv.Itoa):

    entries  before       after
    5,000    7.4s         2.1s
    10,000   17.5s        2.1s
    20,000   48.0s        2.8s
    65,536   >180s (OOM)  3.2s

* Add -interp-loop-limit
2026-05-17 20:42:27 +02:00
deadprogram e80e7e5c10 ci: remove CircleCI configuration
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-05-17 14:36:32 +01:00
pedramktb 6233915ecf targets/wasm_exec: add runtime.getRandomData to gojs imports 2026-05-17 10:25:05 +01:00
Joel Wetzell fd8e0bd70b monitor flag on flash now respects port 2026-05-17 07:14:56 +01:00
Ayke van Laethem 45e2863e9f loader: avoid race condition when loading package list
I was seeing bad error messages... but only some of the time. So I went
digging. Eventually I traced it to this one bug, probably introduced in
https://github.com/tinygo-org/tinygo/pull/5290.

This is not just a visual issue, this is actually a bug in TinyGo that
will result in non-reproducible binaries and random behavior
differences.
2026-05-16 17:24:58 +01:00
Jake Bailey 4204f3d065 sync: add Map.CompareAndSwap and Map.CompareAndDelete
These methods were added in Go 1.20 but were missing from TinyGo's
sync.Map implementation, causing compilation failures for code that
uses them.

The implementation follows the same lock-based approach as the rest
of TinyGo's sync.Map.
2026-05-11 14:13:10 -07:00
Jake Bailey ddbd65beec builder, cgo, syscall: replace fixed-size array casts with unsafe.Slice
Several places used the (*[1 << N]T)(ptr)[:len:len] pattern to convert
C pointers to Go slices. This has a hardcoded size limit that can panic
if exceeded; RunTool hit this with >1024 linker arguments when many
files are embedded.

Replace all instances with unsafe.Slice, which handles any size.
2026-05-11 09:35:04 +02:00
deadprogram 1a1506ef79 builder,loader: fix -ldflags -X not overriding variables with default values
When a string variable already had a source-level default value, the -ldflags
"-X" flag was silently ignored and the variable remained the default value
at runtime.

Fix by stripping the InitOrder entry for each -X variable before
LoadSSA() is called. With no entry in InitOrder, go/ssa emits no init
store, so the global stays zero-valued in the IR. makeGlobalsModule
still injects the actual -X values at final link time.

The -X values remain out of the per-package build cache with only the
variable names appearing in the cache key.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-04-28 11:06:48 +01:00
deadprogram 8793dc37fa modules: update to 'net' package with roundtrip fix for js/wasm
Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-04-27 12:32:43 +01:00
Ayke van Laethem e9d78a78ea compiler: re-enable generics test
This seems to have been removed by accident, but it still seems useful
to me.
2026-04-24 17:48:48 +01:00
68 changed files with 2292 additions and 1241 deletions
-119
View File
@@ -1,119 +0,0 @@
version: 2.1
commands:
submodules:
steps:
- run:
name: "Pull submodules"
command: git submodule update --init
llvm-source-linux:
steps:
- restore_cache:
keys:
- llvm-source-19-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-19-v1
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
- llvm-project/compiler-rt
- llvm-project/lld/include
- llvm-project/llvm/include
hack-ninja-jobs:
steps:
- run:
name: "Hack Ninja to use less jobs"
command: |
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
build-binaryen-linux:
steps:
- restore_cache:
keys:
- binaryen-linux-v3
- run:
name: "Build Binaryen"
command: |
make binaryen
- save_cache:
key: binaryen-linux-v3
paths:
- build/wasm-opt
test-linux:
parameters:
llvm:
type: string
fmt-check:
type: boolean
default: true
steps:
- checkout
- submodules
- run:
name: "Install apt dependencies"
command: |
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
apt-get update
apt-get install --no-install-recommends -y \
llvm-<<parameters.llvm>>-dev \
clang-<<parameters.llvm>> \
libclang-<<parameters.llvm>>-dev \
lld-<<parameters.llvm>> \
cmake \
ninja-build
- hack-ninja-jobs
- build-binaryen-linux
- restore_cache:
keys:
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v4-{{ checksum "go.mod" }}
- llvm-source-linux
- run: go install -tags=llvm<<parameters.llvm>> .
- when:
condition: <<parameters.fmt-check>>
steps:
- run:
# Do this before gen-device so that it doesn't check the
# formatting of generated files.
name: Check Go code formatting
command: make fmt-check lint
- run: make gen-device -j4
# TODO: change this to -skip='TestErrors|TestWasm' with Go 1.20
- run: go test -tags=llvm<<parameters.llvm>> -short -run='TestBuild|TestTest|TestGetList|TestTraceback'
- run: make smoketest XTENSA=0
- save_cache:
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
jobs:
test-oldest:
# This tests our lowest supported versions of Go and LLVM, to make sure at
# least the smoke tests still pass.
docker:
- image: golang:1.23-bullseye
steps:
- test-linux:
llvm: "15"
resource_class: large
test-newest:
# This tests the latest supported LLVM version when linking against system
# libraries.
docker:
- image: golang:1.26-bookworm
steps:
- test-linux:
llvm: "20"
resource_class: large
workflows:
test-all:
jobs:
- test-oldest
# disable this test, since CircleCI seems unable to download due to rate-limits on Dockerhub.
# - test-newest
-1
View File
@@ -1,5 +1,4 @@
build/
llvm-*/
.github
.circleci
+1 -1
View File
@@ -1,6 +1,6 @@
# 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) [![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
[![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)
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
+14 -2
View File
@@ -255,6 +255,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
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
// program so it's pretty fast and doesn't need to be parallelized.
program := lprogram.LoadSSA()
@@ -477,7 +489,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path())
}
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
if err != nil {
return err
}
@@ -1196,7 +1208,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
// needed to convert a program to its final form. Some transformations are not
// optional and must be run as the compiler expects them to run.
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
if err != nil {
return err
}
+1 -1
View File
@@ -28,7 +28,7 @@ func RunTool(tool string, args ...string) error {
var cflag *C.char
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
defer C.free(buf)
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):len(args)]
cflags := unsafe.Slice((**C.char)(buf), len(args))
for i, flag := range args {
cflag := C.CString(flag)
cflags[i] = cflag
+5 -1
View File
@@ -122,12 +122,16 @@ func parseLLDErrors(text string) error {
parsedError = true
line, _ := strconv.Atoi(matches[3])
// 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{
Pos: token.Position{
Filename: matches[2],
Line: line,
},
Msg: "linker could not find symbol " + symbolName,
Msg: msg,
})
}
}
+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.
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
defer C.free(cmdargsC)
cmdargs := (*[1 << 16]*C.char)(cmdargsC)
cmdargs := unsafe.Slice((**C.char)(cmdargsC), len(cflags))
for i, cflag := range cflags {
s := C.CString(cflag)
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.
panic("libclang: file contents was not loaded")
}
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
data := unsafe.Slice((*byte)(unsafe.Pointer(rawData)), size)
// Hash the contents if it isn't hashed yet.
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.
var size C.size_t
sourcePtr := C.clang_getFileContents(tu, file, &size)
source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size]
source := unsafe.Slice((*byte)(unsafe.Pointer(sourcePtr)), size)
lines := []int{0}
for i := 0; i < len(source)-1; i++ {
if source[i] == '\n' {
+40 -39
View File
@@ -21,45 +21,46 @@ var (
// usually passed from the command line, but can also be passed in environment
// variables for example.
type Options struct {
GOOS string // environment variable
GOARCH string // environment variable
GOARM string // environment variable (only used with GOARCH=arm)
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
Target string
BuildMode string // -buildmode flag
Opt string
GC string
PanicStrategy string
Scheduler string
StackSize uint64 // goroutine stack size (if none could be automatically determined)
Serial string
Work bool // -work flag to print temporary build directory
InterpTimeout time.Duration
PrintIR bool
DumpSSA bool
VerifyIR bool
SkipDWARF bool
PrintCommands func(cmd string, args ...string) `json:"-"`
Semaphore chan struct{} `json:"-"` // -p flag controls cap
Debug bool
Nobounds bool
PrintSizes string
PrintAllocs *regexp.Regexp // regexp string
PrintStacks bool
Tags []string
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
TestConfig TestConfig
Programmer string
OpenOCDCommands []string
LLVMFeatures string
Monitor bool
BaudRate int
Timeout time.Duration
WITPackage string // pass through to wasm-tools component embed invocation
WITWorld string // pass through to wasm-tools component embed -w option
ExtLDFlags []string
GoCompatibility bool // enable to check for Go version compatibility
GOOS string // environment variable
GOARCH string // environment variable
GOARM string // environment variable (only used with GOARCH=arm)
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
Target string
BuildMode string // -buildmode flag
Opt string
GC string
PanicStrategy string
Scheduler string
StackSize uint64 // goroutine stack size (if none could be automatically determined)
Serial string
Work bool // -work flag to print temporary build directory
InterpTimeout time.Duration
InterpMaxLoopIterations int
PrintIR bool
DumpSSA bool
VerifyIR bool
SkipDWARF bool
PrintCommands func(cmd string, args ...string) `json:"-"`
Semaphore chan struct{} `json:"-"` // -p flag controls cap
Debug bool
Nobounds bool
PrintSizes string
PrintAllocs *regexp.Regexp // regexp string
PrintStacks bool
Tags []string
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
TestConfig TestConfig
Programmer string
OpenOCDCommands []string
LLVMFeatures string
Monitor bool
BaudRate int
Timeout time.Duration
WITPackage string // pass through to wasm-tools component embed invocation
WITWorld string // pass through to wasm-tools component embed -w option
ExtLDFlags []string
GoCompatibility bool // enable to check for Go version compatibility
}
// Verify performs a validation on the given options, raising an error if options are not valid.
+12 -5
View File
@@ -192,6 +192,16 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
}
}
// Return the runtime.alloc function variant.
// This is normally just "alloc", but is "alloc_noheap" if the //go:noheap
// pragma is used.
func (b *builder) allocFunc() string {
if b.info.noheap {
return "alloc_noheap"
}
return "alloc"
}
type blockInfo struct {
// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
entry llvm.BasicBlock
@@ -314,9 +324,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
// Load comments such as //go:extern on globals.
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 {
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
@@ -2176,7 +2183,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
}
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
layoutValue := b.createObjectLayout(typ, expr.Pos())
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
buf := b.createRuntimeCall(b.allocFunc(), []llvm.Value{sizeValue, layoutValue}, expr.Comment)
align := b.targetData.ABITypeAlignment(typ)
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
return buf, nil
@@ -2408,7 +2415,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
}
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
slicePtr := b.createRuntimeCall(b.allocFunc(), []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
// Extend or truncate if necessary. This is safe as we've already done
+1
View File
@@ -50,6 +50,7 @@ func TestCompiler(t *testing.T) {
{"channel.go", "", ""},
{"gc.go", "", ""},
{"zeromap.go", "", ""},
{"generics.go", "", ""},
}
if goMinor >= 20 {
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)
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
nilPtr := llvm.ConstNull(b.dataPtrType)
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
alloca = b.createRuntimeCall(b.allocFunc(), []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
}
if b.NeedsStackObjects {
b.trackPointer(alloca)
+1 -3
View File
@@ -129,11 +129,9 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
// Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
align := b.targetData.ABITypeAlignment(packedType)
alloc := b.mod.NamedFunction("runtime.alloc")
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
packedAlloc := b.createRuntimeCall(b.allocFunc(), []llvm.Value{
sizeValue,
llvm.ConstNull(b.dataPtrType),
llvm.Undef(b.dataPtrType), // unused context parameter
}, "")
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
if b.NeedsStackObjects {
+454 -167
View File
@@ -3,42 +3,28 @@ package compiler
// This file emits the correct map intrinsics for map operations.
import (
"fmt"
"go/token"
"go/types"
"github.com/tinygo-org/tinygo/src/tinygo"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
const hashArrayUnrollLimit = 4
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
// initializing an appropriately sized object.
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
mapType := expr.Type().Underlying().(*types.Map)
keyType := mapType.Key().Underlying()
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
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)
}
llvmKeyType := b.getLLVMType(keyType)
keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
if expr.Reserve != nil {
sizeHint = b.getValue(expr.Reserve, getPos(expr))
var err error
@@ -47,10 +33,42 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
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
}
// 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
// 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) {
@@ -72,32 +90,23 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// Do the lookup. How it is done depends on the key type.
var commaOkValue llvm.Value
origKeyType := keyType
keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
} else if hashmapIsBinaryKey(keyType) {
// key can be compared with runtime.memequal
// Store the key in an alloca, in the entry block to avoid dynamic stack
// growth.
} else {
// Key stored at actual type: either binary-comparable or with
// compiler-generated hash/equal.
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, mapKeyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
// Fetch the value from the hashmap.
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
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)
fnName := "hashmapBinaryGet"
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericGet"
}
params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
commaOkValue = b.createRuntimeCall(fnName, params, "")
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
}
// Load the resulting value from the hashmap. The value is set to the zero
@@ -120,29 +129,22 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
b.CreateStore(value, valueAlloca)
origKeyType := keyType
keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string
params := []llvm.Value{m, key, valueAlloca}
b.createRuntimeCall("hashmapStringSet", params, "")
} else if hashmapIsBinaryKey(keyType) {
// key can be compared with runtime.memequal
} else {
// Key stored at actual type.
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyAlloca, valueAlloca}
b.createRuntimeCall("hashmapBinarySet", params, "")
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)
fnName := "hashmapBinarySet"
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericSet"
}
params := []llvm.Value{m, itfKey, valueAlloca}
b.createRuntimeCall("hashmapInterfaceSet", params, "")
params := []llvm.Value{m, keyAlloca, valueAlloca}
b.createRuntimeCall(fnName, params, "")
b.emitLifetimeEnd(keyAlloca, keySize)
}
b.emitLifetimeEnd(valueAlloca, valueSize)
}
@@ -150,31 +152,23 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
// createMapDelete deletes a key from a map by calling the appropriate runtime
// 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 {
origKeyType := keyType
keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string
params := []llvm.Value{m, key}
b.createRuntimeCall("hashmapStringDelete", params, "")
return nil
} else if hashmapIsBinaryKey(keyType) {
} else {
// Key stored at actual type.
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyAlloca}
b.createRuntimeCall("hashmapBinaryDelete", params, "")
b.emitLifetimeEnd(keyAlloca, keySize)
return nil
} else {
// Key is not trivially comparable, so compare it as an interface
// instead.
itfKey := key
if _, ok := keyType.(*types.Interface); !ok {
// Not already an interface, so convert it to an interface first.
itfKey = b.createMakeInterface(key, origKeyType, pos)
fnName := "hashmapBinaryDelete"
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericDelete"
}
params := []llvm.Value{m, itfKey}
b.createRuntimeCall("hashmapInterfaceDelete", params, "")
params := []llvm.Value{m, keyAlloca}
b.createRuntimeCall(fnName, params, "")
b.emitLifetimeEnd(keyAlloca, keySize)
return nil
}
}
@@ -195,42 +189,15 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
llvmKeyType := b.getLLVMType(keyType)
llvmValueType := b.getLLVMType(valueType)
// 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")
}
// All key types are now stored at their declared type (no interface wrapping).
// Extract the key and value from the map.
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmKeyType, "range.key")
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
mapKey := b.CreateLoad(llvmKeyType, mapKeyAlloca, "")
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
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.
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
@@ -250,20 +217,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
func hashmapIsBinaryKey(keyType types.Type) bool {
switch keyType := keyType.Underlying().(type) {
case *types.Basic:
// 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
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 || keyType.Kind() == types.UnsafePointer
case *types.Pointer:
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:
return hashmapIsBinaryKey(keyType.Elem())
default:
@@ -271,68 +227,399 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
}
}
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
// we handle nested types. Next, we determine if there are any padding bytes before the next
// element and zero those as well.
// hashmapKeyHashSignature returns the Go type signature for hashmap key hash
// functions: func(key unsafe.Pointer, size, seed uintptr) uint32
func hashmapKeyHashSignature() *types.Signature {
return types.NewSignatureType(nil, nil, nil,
types.NewTuple(
types.NewVar(token.NoPos, nil, "key", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uintptr]),
types.NewVar(token.NoPos, nil, "seed", types.Typ[types.Uintptr]),
),
types.NewTuple(
types.NewVar(token.NoPos, nil, "", types.Typ[types.Uint32]),
),
false,
)
}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
// hashmapKeyEqualSignature returns the Go type signature for hashmap key equal
// functions: func(x, y unsafe.Pointer, n uintptr) bool
func hashmapKeyEqualSignature() *types.Signature {
return types.NewSignatureType(nil, nil, nil,
types.NewTuple(
types.NewVar(token.NoPos, nil, "x", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "y", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "n", types.Typ[types.Uintptr]),
),
types.NewTuple(
types.NewVar(token.NoPos, nil, "", types.Typ[types.Bool]),
),
false,
)
}
switch llvmType.TypeKind() {
case llvm.IntegerTypeKind:
// no padding bytes
return nil
case llvm.PointerTypeKind:
// mo padding bytes
return nil
case llvm.ArrayTypeKind:
llvmArrayType := llvmType
llvmElemType := llvmType.ElementType()
// hashmapKeyFuncName returns a canonical name for a generated hash or equal
// function based on the key type's underlying structure. Named types are
// replaced with their underlying types so that structurally identical key
// types (e.g., struct{i1; str1} and struct{i2; str2} where both i1, i2 are
// int and str1, str2 are string) share the same generated function.
func hashmapKeyFuncName(prefix string, keyType types.Type) string {
return prefix + "." + hashmapCanonicalTypeName(keyType)
}
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this element
b.zeroUndefBytes(llvmElemType, elemPtr)
// hashmapCanonicalTypeName returns a string representation of the hash/equal
// operations needed for a type, stripping named types where the operation does
// not depend on the name. Pointer and channel names do not include the element
// type because their hash/equal operations only use the pointer word.
func hashmapCanonicalTypeName(t types.Type) string {
switch t := t.Underlying().(type) {
case *types.Basic:
return t.Name()
case *types.Pointer:
return "*"
case *types.Chan:
switch t.Dir() {
case types.SendRecv:
return "chan"
case types.SendOnly:
return "chan<-"
case types.RecvOnly:
return "<-chan"
}
case llvm.StructTypeKind:
llvmStructType := llvmType
numFields := llvmStructType.StructElementTypesCount()
llvmElementTypes := llvmStructType.StructElementTypes()
for i := 0; i < numFields; i++ {
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this field
llvmElemType := llvmElementTypes[i]
b.zeroUndefBytes(llvmElemType, elemPtr)
// zero any padding bytes before the next field, if any
offset := b.targetData.ElementOffset(llvmStructType, i)
storeSize := b.targetData.TypeStoreSize(llvmElemType)
fieldEndOffset := offset + storeSize
var nextOffset uint64
if i < numFields-1 {
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
} else {
// Last field? Next offset is the total size of the allocate struct.
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
}
if fieldEndOffset != nextOffset {
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
}
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
// of a key of the given type. The function is generated on first call and
// cached in the module.
func (b *builder) getOrGenerateKeyHashFunc(keyType types.Type) llvm.Value {
name := hashmapKeyFuncName("hashmapKeyHash", keyType)
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
return fn
}
return nil
// Create the LLVM function type:
// (key ptr, size uintptr, seed uintptr, context ptr) -> i32
fnType := llvm.FunctionType(b.ctx.Int32Type(), []llvm.Type{
b.dataPtrType, b.uintptrType, b.uintptrType, b.dataPtrType,
}, false)
fn := llvm.AddFunction(b.mod, name, fnType)
fn.SetLinkage(llvm.LinkOnceODRLinkage)
fn.SetUnnamedAddr(true)
b.addStandardAttributes(fn)
// Generate the function body.
savedBlock := b.GetInsertBlock()
defer b.SetInsertPointAtEnd(savedBlock)
entry := b.ctx.AddBasicBlock(fn, "entry")
b.SetInsertPointAtEnd(entry)
keyPtr := fn.Param(0)
seed := fn.Param(2)
llvmKeyType := b.getLLVMType(keyType)
hash := b.generateKeyHash(keyType, llvmKeyType, keyPtr, seed)
b.CreateRet(hash)
return fn
}
// getOrGenerateKeyEqualFunc returns an LLVM function that compares two keys
// of the given type for equality. The function is generated on first call
// and cached in the module.
func (b *builder) getOrGenerateKeyEqualFunc(keyType types.Type) llvm.Value {
name := hashmapKeyFuncName("hashmapKeyEqual", keyType)
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
return fn
}
// Create the LLVM function type:
// (x ptr, y ptr, n uintptr, context ptr) -> i1
fnType := llvm.FunctionType(b.ctx.Int1Type(), []llvm.Type{
b.dataPtrType, b.dataPtrType, b.uintptrType, b.dataPtrType,
}, false)
fn := llvm.AddFunction(b.mod, name, fnType)
fn.SetLinkage(llvm.LinkOnceODRLinkage)
fn.SetUnnamedAddr(true)
b.addStandardAttributes(fn)
// Generate the function body.
savedBlock := b.GetInsertBlock()
defer b.SetInsertPointAtEnd(savedBlock)
entry := b.ctx.AddBasicBlock(fn, "entry")
b.SetInsertPointAtEnd(entry)
xPtr := fn.Param(0)
yPtr := fn.Param(1)
llvmKeyType := b.getLLVMType(keyType)
result := b.generateKeyEqual(keyType, llvmKeyType, xPtr, yPtr, fn)
b.CreateRet(result)
return fn
}
// generateKeyHash generates IR that hashes a key value. Returns the i32 hash.
func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, keyPtr llvm.Value, seed llvm.Value) llvm.Value {
switch keyType := keyType.Underlying().(type) {
case *types.Basic:
if keyType.Info()&types.IsString != 0 {
// Hash the string contents. The size parameter is unused by
// hashmapStringPtrHash (it dereferences the string header to
// get the actual length), but we pass it for signature
// consistency with other hash functions.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("hashmapStringPtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
}
if keyType.Info()&types.IsFloat != 0 {
// Float hash: normalizes -0 to +0 before hashing.
if keyType.Kind() == types.Float32 {
return b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{keyPtr, seed}, "hash")
}
return b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{keyPtr, seed}, "hash")
}
if keyType.Info()&types.IsComplex != 0 {
// Complex hash: hash real and imaginary parts as floats.
if keyType.Kind() == types.Complex64 {
realPtr := keyPtr
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
llvm.ConstInt(b.uintptrType, 4, false),
}, "")
realHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{realPtr, seed}, "hash.real")
imagHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
return b.CreateXor(realHash, imagHash, "")
}
realPtr := keyPtr
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
llvm.ConstInt(b.uintptrType, 8, false),
}, "")
realHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{realPtr, seed}, "hash.real")
imagHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
return b.CreateXor(realHash, imagHash, "")
}
// Integer/boolean: hash the raw bytes.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
case *types.Pointer, *types.Chan:
// Pointers and channels: hash as raw pointer-sized bytes.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
case *types.Interface:
// Interface: use runtime reflection-based hash.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("hashmapInterfacePtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
case *types.Struct:
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < keyType.NumFields(); i++ {
if keyType.Field(i).Name() == "_" {
continue // blank fields are ignored in Go equality
}
fieldType := keyType.Field(i).Type()
llvmFieldType := b.getLLVMType(fieldType)
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
continue // skip zero-sized fields
}
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
fieldHash := b.generateKeyHash(fieldType, llvmFieldType, fieldPtr, seed)
hash = b.CreateXor(hash, fieldHash, "")
}
return hash
case *types.Array:
elemType := keyType.Elem()
llvmElemType := b.getLLVMType(elemType)
arrayLen := keyType.Len()
if hashmapIsBinaryKey(elemType) {
// All elements are binary-comparable; hash the entire array as raw bytes.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
}
if arrayLen == 0 {
return llvm.ConstInt(b.ctx.Int32Type(), 0, false)
}
if arrayLen <= hashArrayUnrollLimit {
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < int(arrayLen); i++ {
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
hash = b.CreateXor(hash, elemHash, "")
}
return hash
}
initHash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
loopEntry := b.GetInsertBlock()
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.body")
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.done")
b.CreateBr(loopBody)
b.SetInsertPointAtEnd(loopBody)
phiI := b.CreatePHI(b.uintptrType, "i")
phiHash := b.CreatePHI(b.ctx.Int32Type(), "hash.acc")
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, phiI}, "")
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
newHash := b.CreateXor(phiHash, elemHash, "")
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
cond := b.CreateICmp(llvm.IntULT, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
b.CreateCondBr(cond, loopBody, loopDone)
bodyEnd := b.GetInsertBlock()
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
[]llvm.BasicBlock{loopEntry, bodyEnd})
phiHash.AddIncoming([]llvm.Value{initHash, newHash},
[]llvm.BasicBlock{loopEntry, bodyEnd})
b.SetInsertPointAtEnd(loopDone)
return newHash
default:
panic(fmt.Sprintf("unhandled key type for hash generation: %T", keyType))
}
}
// generateKeyEqual generates IR that compares two key values for equality.
// Returns an i1 result.
func (b *builder) generateKeyEqual(keyType types.Type, llvmKeyType llvm.Type, xPtr, yPtr llvm.Value, fn llvm.Value) llvm.Value {
switch keyType := keyType.Underlying().(type) {
case *types.Basic:
if keyType.Info()&types.IsString != 0 {
// Compare strings: load both string headers and compare.
xStr := b.CreateLoad(llvmKeyType, xPtr, "x.str")
yStr := b.CreateLoad(llvmKeyType, yPtr, "y.str")
return b.createRuntimeCall("stringEqual", []llvm.Value{xStr, yStr}, "eq")
}
if keyType.Info()&types.IsFloat != 0 {
// Float equality: fcmp oeq handles -0==+0 (true) and NaN==NaN (false).
xVal := b.CreateLoad(llvmKeyType, xPtr, "x.float")
yVal := b.CreateLoad(llvmKeyType, yPtr, "y.float")
return b.CreateFCmp(llvm.FloatOEQ, xVal, yVal, "eq")
}
if keyType.Info()&types.IsComplex != 0 {
// Complex equality: both real and imaginary parts must be equal.
var floatType llvm.Type
if keyType.Kind() == types.Complex64 {
floatType = b.ctx.FloatType()
} else {
floatType = b.ctx.DoubleType()
}
floatSize := b.targetData.TypeAllocSize(floatType)
imagOffset := llvm.ConstInt(b.uintptrType, floatSize, false)
// Real parts
xReal := b.CreateLoad(floatType, xPtr, "x.real")
yReal := b.CreateLoad(floatType, yPtr, "y.real")
realEq := b.CreateFCmp(llvm.FloatOEQ, xReal, yReal, "eq.real")
// Imaginary parts
xImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), xPtr, []llvm.Value{imagOffset}, "")
yImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), yPtr, []llvm.Value{imagOffset}, "")
xImag := b.CreateLoad(floatType, xImagPtr, "x.imag")
yImag := b.CreateLoad(floatType, yImagPtr, "y.imag")
imagEq := b.CreateFCmp(llvm.FloatOEQ, xImag, yImag, "eq.imag")
return b.CreateAnd(realEq, imagEq, "")
}
// Integer/boolean: compare raw bytes.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
case *types.Pointer, *types.Chan:
// Pointers and channels: compare as raw pointer-sized bytes.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
case *types.Interface:
// Interface: use runtime interface equality.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("hashmapInterfaceEqual", []llvm.Value{xPtr, yPtr, size}, "eq")
case *types.Struct:
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false) // start with true
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < keyType.NumFields(); i++ {
if keyType.Field(i).Name() == "_" {
continue // blank fields are ignored in Go equality
}
fieldType := keyType.Field(i).Type()
llvmFieldType := b.getLLVMType(fieldType)
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
continue // skip zero-sized fields
}
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
xFieldPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
yFieldPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
fieldEq := b.generateKeyEqual(fieldType, llvmFieldType, xFieldPtr, yFieldPtr, fn)
result = b.CreateAnd(result, fieldEq, "")
}
return result
case *types.Array:
elemType := keyType.Elem()
llvmElemType := b.getLLVMType(elemType)
arrayLen := keyType.Len()
if hashmapIsBinaryKey(elemType) {
// All elements are binary-comparable; compare the entire array.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
}
if arrayLen == 0 {
return llvm.ConstInt(b.ctx.Int1Type(), 1, false)
}
if arrayLen <= hashArrayUnrollLimit {
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < int(arrayLen); i++ {
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
result = b.CreateAnd(result, elemEq, "")
}
return result
}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
loopEntry := b.GetInsertBlock()
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.body")
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.done")
b.CreateBr(loopBody)
b.SetInsertPointAtEnd(loopBody)
phiI := b.CreatePHI(b.uintptrType, "i")
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, phiI}, "")
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, phiI}, "")
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
atEnd := b.CreateICmp(llvm.IntUGE, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
exitLoop := b.CreateOr(atEnd, b.CreateNot(elemEq, ""), "")
b.CreateCondBr(exitLoop, loopDone, loopBody)
bodyEnd := b.GetInsertBlock()
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
[]llvm.BasicBlock{loopEntry, bodyEnd})
b.SetInsertPointAtEnd(loopDone)
return elemEq
default:
panic(fmt.Sprintf("unhandled key type for equal generation: %T", keyType))
}
}
+30 -1
View File
@@ -34,6 +34,7 @@ type functionInfo struct {
interrupt bool // go:interrupt
nobounds bool // go:nobounds
noescape bool // go:noescape
noheap bool // go:noheap
variadic bool // go:variadic (CGo only)
inline inlineType // go:inline
}
@@ -161,7 +162,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "runtime.alloc":
case "runtime.alloc", "runtime.alloc_noheap":
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
// returns values that are never null and never alias to an existing value.
for _, attrName := range []string{"noalias", "nonnull"} {
@@ -190,6 +191,31 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
case "runtime.stringFromRunes":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 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":
// This function is necessary for tracking pointers on the stack in a
// portable way (see gc_stack_portable.go). Indicate to the optimizer
@@ -451,6 +477,9 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
if len(f.Blocks) == 0 {
info.noescape = true
}
case "//go:noheap":
// Ensure this function does not allocate on the heap.
info.noheap = true
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
+29 -33
View File
@@ -10,33 +10,30 @@ target triple = "wasm32-unknown-wasi"
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
entry:
%0 = add i32 %x, %y
ret i32 %0
}
; Function Attrs: nounwind
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
entry:
%0 = icmp eq i32 %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -50,14 +47,14 @@ divbyzero.next: ; preds = %entry
ret i32 %5
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #3
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
declare void @runtime.divideByZeroPanic(ptr) #1
declare void @runtime.divideByZeroPanic(ptr) #0
; Function Attrs: nounwind
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -67,12 +64,12 @@ divbyzero.next: ; preds = %entry
ret i32 %1
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #3
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
; Function Attrs: nounwind
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -86,12 +83,12 @@ divbyzero.next: ; preds = %entry
ret i32 %5
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #3
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
; Function Attrs: nounwind
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
entry:
%0 = icmp eq i32 %y, 0
br i1 %0, label %divbyzero.throw, label %divbyzero.next
@@ -101,66 +98,66 @@ divbyzero.next: ; preds = %entry
ret i32 %1
divbyzero.throw: ; preds = %entry
call void @runtime.divideByZeroPanic(ptr undef) #3
call void @runtime.divideByZeroPanic(ptr undef) #2
unreachable
}
; Function Attrs: nounwind
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #2 {
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 {
entry:
%0 = fcmp oeq float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #2 {
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 {
entry:
%0 = fcmp une float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #2 {
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 {
entry:
%0 = fcmp olt float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
entry:
%0 = fcmp ole float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #2 {
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 {
entry:
%0 = fcmp ogt float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
entry:
%0 = fcmp oge float %x, %y
ret i1 %0
}
; Function Attrs: nounwind
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
entry:
ret float %x.r
}
; Function Attrs: nounwind
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
entry:
ret float %x.i
}
; Function Attrs: nounwind
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
entry:
%0 = fadd float %x.r, %y.r
%1 = fadd float %x.i, %y.i
@@ -170,7 +167,7 @@ entry:
}
; Function Attrs: nounwind
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
entry:
%0 = fsub float %x.r, %y.r
%1 = fsub float %x.i, %y.i
@@ -180,7 +177,7 @@ entry:
}
; Function Attrs: nounwind
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
entry:
%0 = fmul float %x.r, %y.r
%1 = fmul float %x.i, %y.i
@@ -194,19 +191,18 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
define hidden void @main.foo(ptr %context) unnamed_addr #1 {
entry:
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
ret void
}
; Function Attrs: nounwind
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
entry:
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
attributes #3 = { nounwind }
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 }
+21 -25
View File
@@ -6,76 +6,73 @@ target triple = "wasm32-unknown-wasi"
%runtime.channelOp = type { ptr, ptr, i32, ptr }
%runtime.chanSelectState = type { ptr, ptr }
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
entry:
%chan.op = alloca %runtime.channelOp, align 8
%chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
store i32 3, ptr %chan.value, align 4
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
ret void
}
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
; Function Attrs: nounwind
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
entry:
%chan.op = alloca %runtime.channelOp, align 8
%chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
ret void
}
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
; Function Attrs: nounwind
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
entry:
%chan.op = alloca %runtime.channelOp, align 8
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
ret void
}
; Function Attrs: nounwind
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
entry:
%chan.op = alloca %runtime.channelOp, align 8
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
ret void
}
; Function Attrs: nounwind
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 {
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #1 {
entry:
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
%select.send.value = alloca i32, align 4
@@ -88,7 +85,7 @@ entry:
store ptr %ch2, ptr %0, align 4
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
store ptr null, ptr %.repack3, align 4
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #4
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #3
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
%1 = extractvalue { i32, i1 } %select.result, 0
%2 = icmp eq i32 %1, 0
@@ -105,10 +102,9 @@ select.body: ; preds = %select.next
br label %select.done
}
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #0
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #4 = { nounwind }
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 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #3 = { nounwind }
+23 -23
View File
@@ -6,19 +6,16 @@ target triple = "thumbv7m-unknown-unknown-eabi"
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
%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
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(ptr %context) unnamed_addr #0 {
entry:
ret void
}
declare void @main.external(ptr) #2
declare void @main.external(ptr) #1
; Function Attrs: nounwind
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
define hidden void @main.deferSimple(ptr %context) unnamed_addr #0 {
entry:
%defer.alloca = alloca { i32, ptr }, align 4
%deferPtr = alloca ptr, align 4
@@ -112,14 +109,14 @@ rundefers.end3: ; preds = %rundefers.loophead6
}
; Function Attrs: nocallback nofree nosync nounwind willreturn
declare ptr @llvm.stacksave.p0() #3
declare ptr @llvm.stacksave.p0() #2
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #1
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #1
; Function Attrs: nounwind
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 {
entry:
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 3, ptr undef) #4
@@ -127,14 +124,14 @@ entry:
ret void
}
declare void @runtime.printlock(ptr) #2
declare void @runtime.printlock(ptr) #1
declare void @runtime.printint32(i32, ptr) #2
declare void @runtime.printint32(i32, ptr) #1
declare void @runtime.printunlock(ptr) #2
declare void @runtime.printunlock(ptr) #1
; Function Attrs: nounwind
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #0 {
entry:
%defer.alloca2 = alloca { i32, ptr }, align 4
%defer.alloca = alloca { i32, ptr }, align 4
@@ -253,7 +250,7 @@ rundefers.end7: ; preds = %rundefers.loophead1
}
; Function Attrs: nounwind
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #0 {
entry:
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 3, ptr undef) #4
@@ -262,7 +259,7 @@ entry:
}
; Function Attrs: nounwind
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #0 {
entry:
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 5, ptr undef) #4
@@ -271,7 +268,7 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 {
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #0 {
entry:
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
@@ -315,8 +312,11 @@ rundefers.end: ; preds = %rundefers.loophead
br label %recover
}
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #3
; Function Attrs: nounwind
define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
define hidden void @main.deferLoop(ptr %context) unnamed_addr #0 {
entry:
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
@@ -405,7 +405,7 @@ rundefers.end1: ; preds = %rundefers.loophead4
}
; Function Attrs: nounwind
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #0 {
entry:
%defer.alloca = alloca { i32, ptr, i32 }, align 4
%deferPtr = alloca ptr, align 4
@@ -505,9 +505,9 @@ rundefers.end4: ; preds = %rundefers.loophead7
br label %recover
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #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 = { "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 #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #2 = { nocallback nofree nosync nounwind willreturn }
attributes #3 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #4 = { nounwind }
attributes #5 = { nounwind returns_twice }
+12 -16
View File
@@ -3,19 +3,16 @@ source_filename = "float.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #2 {
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -27,25 +24,25 @@ entry:
}
; Function Attrs: nounwind
define hidden float @main.maxu32f(ptr %context) unnamed_addr #2 {
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 {
entry:
ret float 0x41F0000000000000
}
; Function Attrs: nounwind
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #2 {
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 {
entry:
ret i32 -1
}
; Function Attrs: nounwind
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #2 {
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #1 {
entry:
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
}
; Function Attrs: nounwind
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #2 {
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 {
entry:
%0 = uitofp i32 %v to float
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
@@ -55,7 +52,7 @@ entry:
}
; Function Attrs: nounwind
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #2 {
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -68,7 +65,7 @@ entry:
}
; Function Attrs: nounwind
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #2 {
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 {
entry:
%positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 2.550000e+02
@@ -80,7 +77,7 @@ entry:
}
; Function Attrs: nounwind
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #2 {
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 {
entry:
%abovemin = fcmp oge float %v, -1.280000e+02
%belowmax = fcmp ole float %v, 1.270000e+02
@@ -93,6 +90,5 @@ entry:
ret i8 %0
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
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" }
+11 -15
View File
@@ -3,48 +3,44 @@ source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #2 {
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 {
entry:
%0 = icmp eq ptr %callback.funcptr, null
br i1 %0, label %fpcall.throw, label %fpcall.next
fpcall.next: ; preds = %entry
call void %callback.funcptr(i32 3, ptr %callback.context) #3
call void %callback.funcptr(i32 3, ptr %callback.context) #2
ret void
fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(ptr undef) #3
call void @runtime.nilPanic(ptr undef) #2
unreachable
}
declare void @runtime.nilPanic(ptr) #1
declare void @runtime.nilPanic(ptr) #0
; Function Attrs: nounwind
define hidden void @main.bar(ptr %context) unnamed_addr #2 {
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
entry:
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
ret void
}
; Function Attrs: nounwind
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #2 {
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 {
entry:
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
attributes #3 = { nounwind }
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 }
+14 -14
View File
@@ -25,19 +25,16 @@ target triple = "wasm32-unknown-wasi"
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
entry:
%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
@@ -55,8 +52,11 @@ entry:
ret void
}
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
; Function Attrs: nounwind
define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
entry:
%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
@@ -72,7 +72,7 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%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
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
entry:
%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
@@ -103,7 +103,7 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
entry:
%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
@@ -125,7 +125,7 @@ entry:
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%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
}
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 = { "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 #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 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind }
+78 -160
View File
@@ -1,144 +1,78 @@
; ModuleID = 'generics.go'
source_filename = "generics.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%"main.Point[int]" = type { i32, i32 }
%"main.Point[float32]" = type { float, float }
%"main.Point[int]" = type { i32, i32 }
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.main(i8* %context) unnamed_addr #1 {
define hidden void @main.main(ptr %context) unnamed_addr #1 {
entry:
%bi = alloca %"main.Point[int]", align 8
%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)
%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)
%1 = call %"main.Point[int]" @"main.Add[int]"(i32 0, i32 0, i32 0, i32 0, ptr undef)
ret void
}
; Function Attrs: nounwind
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, i8* %context) unnamed_addr #1 {
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, ptr %context) unnamed_addr #1 {
entry:
%complit = alloca %"main.Point[float32]", align 8
%b = alloca %"main.Point[float32]", align 8
%a = alloca %"main.Point[float32]", align 8
%a.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
store float 0.000000e+00, float* %a.repack, align 8
%a.repack9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
store float 0.000000e+00, float* %a.repack9, align 4
%0 = bitcast %"main.Point[float32]"* %a to i8*
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
%a.repack10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
store float %a.X, float* %a.repack10, align 8
%a.repack11 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
store float %a.Y, float* %a.repack11, align 4
%b.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
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
%stackalloc = alloca i8, align 1
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3
store float %a.X, ptr %a, align 4
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4
store float %a.Y, ptr %a.repack9, align 4
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3
store float %b.X, ptr %b, align 4
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4
store float %b.Y, ptr %b.repack11, align 4
call void @main.checkSize(i32 4, ptr undef) #3
call void @main.checkSize(i32 8, ptr undef) #3
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
br i1 false, label %deref.throw, label %deref.next
deref.next: ; preds = %entry
br i1 false, label %deref.throw1, label %deref.next2
deref.next2: ; preds = %deref.next
%4 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
%5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
%6 = load float, float* %5, align 8
%7 = load float, float* %4, align 8
%8 = fadd float %6, %7
%0 = load float, ptr %a, align 4
%1 = load float, ptr %b, align 4
%2 = fadd float %0, %1
br i1 false, label %deref.throw3, label %deref.next4
deref.next4: ; preds = %deref.next2
br i1 false, label %deref.throw5, label %deref.next6
deref.next6: ; preds = %deref.next4
%9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
%10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
%11 = load float, float* %10, align 4
%12 = load float, float* %9, align 4
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
%5 = load float, ptr %4, align 4
%6 = load float, ptr %3, align 4
br i1 false, label %store.throw, label %store.next
store.next: ; preds = %deref.next6
store float %8, float* %3, align 8
store float %2, ptr %complit, align 4
br i1 false, label %store.throw7, label %store.next8
store.next8: ; preds = %store.next
%13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
%14 = fadd float %11, %12
store float %14, float* %13, align 4
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
%.unpack = load float, float* %.elt, align 8
%15 = insertvalue %"main.Point[float32]" undef, float %.unpack, 0
%16 = insertvalue %"main.Point[float32]" %15, float %14, 1
ret %"main.Point[float32]" %16
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
%8 = fadd float %5, %6
store float %8, ptr %7, align 4
%.unpack = load float, ptr %complit, align 4
%9 = insertvalue %"main.Point[float32]" poison, float %.unpack, 0
%10 = insertvalue %"main.Point[float32]" %9, float %8, 1
ret %"main.Point[float32]" %10
deref.throw: ; preds = %entry
unreachable
@@ -159,81 +93,64 @@ store.throw7: ; preds = %store.next
unreachable
}
declare void @main.checkSize(i32, i8*) #0
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
declare void @runtime.nilPanic(i8*) #0
declare void @main.checkSize(i32, ptr) #0
declare void @runtime.nilPanic(ptr) #0
; Function Attrs: nounwind
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, i8* %context) unnamed_addr #1 {
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 {
entry:
%complit = alloca %"main.Point[int]", align 8
%b = alloca %"main.Point[int]", align 8
%a = alloca %"main.Point[int]", align 8
%a.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
store i32 0, i32* %a.repack, align 8
%a.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
store i32 0, i32* %a.repack9, align 4
%0 = bitcast %"main.Point[int]"* %a to i8*
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
%a.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
store i32 %a.X, i32* %a.repack10, align 8
%a.repack11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
store i32 %a.Y, i32* %a.repack11, align 4
%b.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
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
%stackalloc = alloca i8, align 1
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #3
store i32 %a.X, ptr %a, align 4
%a.repack9 = getelementptr inbounds nuw i8, ptr %a, i32 4
store i32 %a.Y, ptr %a.repack9, align 4
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #3
store i32 %b.X, ptr %b, align 4
%b.repack11 = getelementptr inbounds nuw i8, ptr %b, i32 4
store i32 %b.Y, ptr %b.repack11, align 4
call void @main.checkSize(i32 4, ptr undef) #3
call void @main.checkSize(i32 8, ptr undef) #3
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
br i1 false, label %deref.throw, label %deref.next
deref.next: ; preds = %entry
br i1 false, label %deref.throw1, label %deref.next2
deref.next2: ; preds = %deref.next
%4 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
%5 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
%6 = load i32, i32* %5, align 8
%7 = load i32, i32* %4, align 8
%8 = add i32 %6, %7
%0 = load i32, ptr %a, align 4
%1 = load i32, ptr %b, align 4
%2 = add i32 %0, %1
br i1 false, label %deref.throw3, label %deref.next4
deref.next4: ; preds = %deref.next2
br i1 false, label %deref.throw5, label %deref.next6
deref.next6: ; preds = %deref.next4
%9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
%10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
%11 = load i32, i32* %10, align 4
%12 = load i32, i32* %9, align 4
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
%5 = load i32, ptr %4, align 4
%6 = load i32, ptr %3, align 4
br i1 false, label %store.throw, label %store.next
store.next: ; preds = %deref.next6
store i32 %8, i32* %3, align 8
store i32 %2, ptr %complit, align 4
br i1 false, label %store.throw7, label %store.next8
store.next8: ; preds = %store.next
%13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
%14 = add i32 %11, %12
store i32 %14, i32* %13, align 4
%.elt = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
%.unpack = load i32, i32* %.elt, align 8
%15 = insertvalue %"main.Point[int]" undef, i32 %.unpack, 0
%16 = insertvalue %"main.Point[int]" %15, i32 %14, 1
ret %"main.Point[int]" %16
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
%8 = add i32 %5, %6
store i32 %8, ptr %7, align 4
%.unpack = load i32, ptr %complit, align 4
%9 = insertvalue %"main.Point[int]" poison, i32 %.unpack, 0
%10 = insertvalue %"main.Point[int]" %9, i32 %8, 1
ret %"main.Point[int]" %10
deref.throw: ; preds = %entry
unreachable
@@ -254,6 +171,7 @@ store.throw7: ; preds = %store.next
unreachable
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
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 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nounwind }
+13 -17
View File
@@ -5,27 +5,24 @@ target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 }
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %s.data
}
; Function Attrs: nounwind
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = icmp slt i16 %len, 0
@@ -39,25 +36,24 @@ unsafe.String.next: ; preds = %entry
%5 = zext nneg i16 %len to i32
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
%7 = insertvalue %runtime._string %6, i32 %5, 1
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
ret %runtime._string %7
unsafe.String.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #3
call void @runtime.unsafeSlicePanic(ptr undef) #2
unreachable
}
declare void @runtime.unsafeSlicePanic(ptr) #1
declare void @runtime.unsafeSlicePanic(ptr) #0
; Function Attrs: nounwind
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %s.data
}
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 = { "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 #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 }
+39 -43
View File
@@ -5,35 +5,32 @@ target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 }
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #1 {
entry:
ret i32 %a
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #3
declare i32 @llvm.smin.i32(i32, i32) #2
; Function Attrs: nounwind
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #1 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
@@ -41,7 +38,7 @@ entry:
}
; Function Attrs: nounwind
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #1 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
@@ -50,109 +47,109 @@ entry:
}
; Function Attrs: nounwind
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
ret i8 %0
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.umin.i8(i8, i8) #3
declare i8 @llvm.umin.i8(i8, i8) #2
; Function Attrs: nounwind
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #3
declare i32 @llvm.umin.i32(i32, i32) #2
; Function Attrs: nounwind
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call float @llvm.minimum.f32(float %a, float %b)
ret float %0
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.minimum.f32(float, float) #3
declare float @llvm.minimum.f32(float, float) #2
; Function Attrs: nounwind
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call double @llvm.minimum.f64(double %a, double %b)
ret double %0
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare double @llvm.minimum.f64(double, double) #3
declare double @llvm.minimum.f64(double, double) #2
; Function Attrs: nounwind
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
%stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #4
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = select i1 %4, ptr %a.data, ptr %b.data
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
ret %runtime._string %5
}
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
; Function Attrs: nounwind
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smax.i32(i32, i32) #3
declare i32 @llvm.smax.i32(i32, i32) #2
; Function Attrs: nounwind
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umax.i32(i32, i32) #3
declare i32 @llvm.umax.i32(i32, i32) #2
; Function Attrs: nounwind
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
entry:
%0 = call float @llvm.maximum.f32(float %a, float %b)
ret float %0
}
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.maximum.f32(float, float) #3
declare float @llvm.maximum.f32(float, float) #2
; Function Attrs: nounwind
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
%stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #4
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = select i1 %4, ptr %a.data, ptr %b.data
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
ret %runtime._string %5
}
; Function Attrs: nounwind
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
entry:
%0 = shl i32 %s.len, 2
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
@@ -160,26 +157,25 @@ entry:
}
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #4
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
; Function Attrs: nounwind
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
define hidden void @main.clearMap(ptr dereferenceable_or_null(48) %m, ptr %context) unnamed_addr #1 {
entry:
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
call void @runtime.hashmapClear(ptr %m, ptr undef) #4
ret void
}
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(48), ptr) #0
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #5 = { nounwind }
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 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #4 = { nounwind }
+28 -28
View File
@@ -5,39 +5,36 @@ target triple = "thumbv7m-unknown-unknown-eabi"
@"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
define hidden void @main.init(ptr %context) unnamed_addr #1 {
define hidden void @main.init(ptr %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #0 {
entry:
%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
ret void
}
declare void @main.regularFunction(i32, ptr) #2
declare void @main.regularFunction(i32, ptr) #1
; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
ret void
}
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #2
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #1
declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #0 {
entry:
%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
@@ -45,13 +42,13 @@ entry:
}
; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
@@ -59,7 +56,7 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 {
entry:
%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
@@ -76,8 +73,11 @@ entry:
ret void
}
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
entry:
store i32 7, ptr %context, align 4
ret void
@@ -93,14 +93,14 @@ entry:
ret void
}
declare void @runtime.printlock(ptr) #2
declare void @runtime.printlock(ptr) #1
declare void @runtime.printint32(i32, ptr) #2
declare void @runtime.printint32(i32, ptr) #1
declare void @runtime.printunlock(ptr) #2
declare void @runtime.printunlock(ptr) #1
; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #0 {
entry:
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
store i32 5, ptr %0, align 4
@@ -126,13 +126,13 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #0 {
entry:
%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)
@@ -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
; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #0 {
entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #11
ret void
}
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #0 {
entry:
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
store ptr %itf.value, ptr %0, align 4
@@ -186,11 +186,11 @@ entry:
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #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 = { "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.regularFunction" }
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 #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #2 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #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) }
+29 -29
View File
@@ -5,30 +5,27 @@ target triple = "wasm32-unknown-wasi"
@"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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
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
ret void
}
declare void @main.regularFunction(i32, ptr) #1
declare void @main.regularFunction(i32, ptr) #0
declare void @runtime.deadlock(ptr) #1
declare void @runtime.deadlock(ptr) #0
; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
@@ -36,23 +33,23 @@ entry:
unreachable
}
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
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
ret void
}
; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
@@ -61,7 +58,7 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry:
%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
@@ -82,8 +79,11 @@ entry:
ret void
}
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
entry:
store i32 7, ptr %context, align 4
ret void
@@ -100,14 +100,14 @@ entry:
unreachable
}
declare void @runtime.printlock(ptr) #1
declare void @runtime.printlock(ptr) #0
declare void @runtime.printint32(i32, ptr) #1
declare void @runtime.printint32(i32, ptr) #0
declare void @runtime.printunlock(ptr) #1
declare void @runtime.printunlock(ptr) #0
; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
@@ -135,13 +135,13 @@ entry:
}
; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #2 {
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
entry:
%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)
@@ -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
; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #11
ret void
}
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #0
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
@@ -197,11 +197,11 @@ entry:
unreachable
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
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 = { 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 #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" "tinygo-gowrapper"="main.regularFunction" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #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) }
+37 -41
View File
@@ -19,57 +19,54 @@ target triple = "wasm32-unknown-wasi"
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
@"reflect/types.typeid:basic:int" = external constant i8
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
}
; Function Attrs: nounwind
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
}
; Function Attrs: nounwind
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -79,12 +76,12 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
; Function Attrs: nounwind
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -94,12 +91,12 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
; Function Attrs: nounwind
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -109,34 +106,33 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #6
ret i8 %0
}
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #4
; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #6
%1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._string %0
}
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
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-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"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
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 }
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 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #6 = { nounwind }
+12 -16
View File
@@ -3,48 +3,44 @@ source_filename = "pointer.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #2 {
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #1 {
entry:
ret [0 x i32] zeroinitializer
}
; Function Attrs: nounwind
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #2 {
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %x
}
; Function Attrs: nounwind
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #2 {
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %x
}
; Function Attrs: nounwind
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #2 {
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
ret ptr %x
}
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 = { "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 #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 }
+5
View File
@@ -115,3 +115,8 @@ func doesNotEscapeParam(a *int, b []int, c chan int, d *[0]byte)
//go:noescape
func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
}
//go:noheap
func doesHeapAlloc() *int {
return new(int)
}
+40 -30
View File
@@ -11,95 +11,105 @@ target triple = "wasm32-unknown-wasi"
@undefinedGlobalNotInSection = external global i32, align 4
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define void @extern_func() #3 {
define void @extern_func() #2 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #2 {
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 {
entry:
ret void
}
declare void @somepkg.someFunction2(ptr) #1
declare void @somepkg.someFunction2(ptr) #0
; Function Attrs: inlinehint nounwind
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #4 {
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 {
entry:
ret void
}
; Function Attrs: noinline nounwind
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #5 {
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.useGeneric(ptr %context) unnamed_addr #2 {
define hidden void @main.useGeneric(ptr %context) unnamed_addr #1 {
entry:
call void @"main.noinlineGenericFunc[int8]"(ptr undef)
ret void
}
; Function Attrs: noinline nounwind
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #5 {
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #4 {
entry:
ret void
}
; Function Attrs: noinline nounwind
define hidden void @main.functionInSection(ptr %context) unnamed_addr #5 section ".special_function_section" {
define hidden void @main.functionInSection(ptr %context) unnamed_addr #4 section ".special_function_section" {
entry:
ret void
}
; Function Attrs: noinline nounwind
define void @exportedFunctionInSection() #6 section ".special_function_section" {
define void @exportedFunctionInSection() #5 section ".special_function_section" {
entry:
ret void
}
declare void @main.declaredImport() #7
declare void @main.declaredImport() #6
declare void @imported() #8
declare void @imported() #7
; Function Attrs: nounwind
define void @exported() #9 {
define void @exported() #8 {
entry:
ret void
}
declare void @main.undefinedFunctionNotInSection(ptr) #1
declare void @main.undefinedFunctionNotInSection(ptr) #0
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #1
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #0
; Function Attrs: nounwind
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #2 {
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #1 {
entry:
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
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" }
attributes #4 = { inlinehint 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" }
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" }
attributes #9 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
; Function Attrs: nounwind
define hidden ptr @main.doesHeapAlloc(ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%new = call align 4 dereferenceable(4) ptr @runtime.alloc_noheap(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #10
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #10
ret ptr %new
}
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc_noheap(i32, ptr, ptr) #9
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
attributes #8 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
attributes #9 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #10 = { nounwind }
+31 -31
View File
@@ -3,31 +3,28 @@ source_filename = "slice.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
entry:
ret i32 %ints.len
}
; Function Attrs: nounwind
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
entry:
ret i32 %ints.cap
}
; Function Attrs: nounwind
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #2 {
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #1 {
entry:
%.not = icmp ult i32 %index, %ints.len
br i1 %.not, label %lookup.next, label %lookup.throw
@@ -42,10 +39,10 @@ lookup.throw: ; preds = %entry
unreachable
}
declare void @runtime.lookupPanic(ptr) #1
declare void @runtime.lookupPanic(ptr) #0
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
entry:
%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
@@ -66,10 +63,13 @@ entry:
ret { ptr, i32, i32 } %4
}
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #0
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #1 {
entry:
%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
@@ -84,7 +84,7 @@ entry:
}
; Function Attrs: nounwind
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
entry:
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
%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
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp slt i32 %len, 0
@@ -118,10 +118,10 @@ slice.throw: ; preds = %entry
unreachable
}
declare void @runtime.slicePanic(ptr) #1
declare void @runtime.slicePanic(ptr) #0
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp slt i32 %len, 0
@@ -142,7 +142,7 @@ slice.throw: ; preds = %entry
}
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp ugt i32 %len, 1431655765
@@ -163,7 +163,7 @@ slice.throw: ; preds = %entry
}
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%slice.maxcap = icmp ugt i32 %len, 1073741823
@@ -184,7 +184,7 @@ slice.throw: ; preds = %entry
}
; Function Attrs: nounwind
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = getelementptr i8, ptr %p, i32 %len
@@ -193,7 +193,7 @@ entry:
}
; Function Attrs: nounwind
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #2 {
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = trunc i64 %len to i32
@@ -203,7 +203,7 @@ entry:
}
; Function Attrs: nounwind
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
entry:
%0 = icmp ult i32 %s.len, 4
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
@@ -216,10 +216,10 @@ slicetoarray.throw: ; preds = %entry
unreachable
}
declare void @runtime.sliceToArrayPointerPanic(ptr) #1
declare void @runtime.sliceToArrayPointerPanic(ptr) #0
; Function Attrs: nounwind
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 {
entry:
%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
@@ -234,7 +234,7 @@ slicetoarray.throw: ; preds = %entry
}
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = icmp ugt i32 %len, 1073741823
@@ -256,10 +256,10 @@ unsafe.Slice.throw: ; preds = %entry
unreachable
}
declare void @runtime.unsafeSlicePanic(ptr) #1
declare void @runtime.unsafeSlicePanic(ptr) #0
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = icmp eq ptr %ptr, null
@@ -281,7 +281,7 @@ unsafe.Slice.throw: ; preds = %entry
}
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = icmp ugt i64 %len, 1073741823
@@ -305,7 +305,7 @@ unsafe.Slice.throw: ; preds = %entry
}
; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 {
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
entry:
%stackalloc = alloca i8, align 1
%0 = icmp ugt i64 %len, 1073741823
@@ -328,9 +328,9 @@ unsafe.Slice.throw: ; preds = %entry
unreachable
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
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 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #5 = { nounwind }
+21 -25
View File
@@ -7,37 +7,34 @@ target triple = "wasm32-unknown-wasi"
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #2 {
define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #1 {
entry:
ret %runtime._string { ptr @"main$string", i32 3 }
}
; Function Attrs: nounwind
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #2 {
define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #1 {
entry:
ret %runtime._string zeroinitializer
}
; Function Attrs: nounwind
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
entry:
ret i32 %s.len
}
; Function Attrs: nounwind
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #1 {
entry:
%.not = icmp ult i32 %index, %s.len
br i1 %.not, label %lookup.next, label %lookup.throw
@@ -48,40 +45,40 @@ lookup.next: ; preds = %entry
ret i8 %1
lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(ptr undef) #3
call void @runtime.lookupPanic(ptr undef) #2
unreachable
}
declare void @runtime.lookupPanic(ptr) #1
declare void @runtime.lookupPanic(ptr) #0
; Function Attrs: nounwind
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2
ret i1 %0
}
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0
; Function Attrs: nounwind
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #2
%1 = xor i1 %0, true
ret i1 %1
}
; Function Attrs: nounwind
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 {
entry:
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #2
ret i1 %0
}
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
; Function Attrs: nounwind
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #1 {
entry:
%0 = zext i8 %x to i32
%.not = icmp ugt i32 %s.len, %0
@@ -93,11 +90,10 @@ lookup.next: ; preds = %entry
ret i8 %2
lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(ptr undef) #3
call void @runtime.lookupPanic(ptr undef) #2
unreachable
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
attributes #3 = { nounwind }
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 }
+24 -54
View File
@@ -5,19 +5,16 @@ target triple = "wasm32-unknown-wasi"
%main.hasPadding = type { i1, i32, i1 }
; 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
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
@@ -27,29 +24,23 @@ entry:
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 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
%3 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
%6 = load i32, ptr %hashmap.value, align 4
%4 = load i32, ptr %hashmap.value, align 4
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret i32 %6
ret i32 %4
}
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
declare void @runtime.memzero(ptr, i32, ptr) #1
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
; Function Attrs: noinline nounwind
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 {
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
@@ -60,20 +51,16 @@ entry:
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 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 @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret void
}
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #1
declare void @runtime.hashmapGenericSet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, ptr) #0
; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
@@ -84,23 +71,15 @@ entry:
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
%0 = 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
%0 = call i1 @runtime.hashmapGenericGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
%5 = load i32, ptr %hashmap.value, align 4
%1 = load i32, ptr %hashmap.value, align 4
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret i32 %5
ret i32 %1
}
; Function Attrs: noinline nounwind
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 {
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
@@ -112,29 +91,20 @@ entry:
%hashmap.key.repack1 = getelementptr inbounds nuw i8, ptr %hashmap.key, i32 12
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
%0 = 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 @runtime.hashmapGenericSet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret void
}
; Function Attrs: nounwind
define hidden void @main.main(ptr %context) unnamed_addr #2 {
define hidden void @main.main(ptr %context) unnamed_addr #1 {
entry:
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #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" }
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 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #5 = { nounwind }
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 = { noinline nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #4 = { nounwind }
+1
View File
@@ -38,6 +38,7 @@ func TestErrors(t *testing.T) {
{name: "loader-invaliddep"},
{name: "loader-invalidpackage"},
{name: "loader-nopackage"},
{name: "noheap", target: "cortex-m-qemu"},
{name: "optimizer"},
{name: "syntax"},
{name: "types"},
+2 -1
View File
@@ -19,6 +19,7 @@ var (
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
errMapAlreadyCreated = errors.New("interp: map already created")
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
@@ -29,7 +30,7 @@ var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not
func isRecoverableError(err error) bool {
return err == errIntegerAsPointer || err == errUnsupportedInst ||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
err == errLoopUnrolled || err == errInvalidPtrToIntSize
err == errLoopUnrolled || err == errLoopTooLong || err == errInvalidPtrToIntSize
}
// ErrorLine is one line in a traceback. The position may be missing.
+33 -30
View File
@@ -19,35 +19,38 @@ const checks = true
// runner contains all state related to one interp run.
type runner struct {
mod llvm.Module
targetData llvm.TargetData
builder llvm.Builder
pointerSize uint32 // cached pointer size from the TargetData
dataPtrType llvm.Type // often used type so created in advance
uintptrType llvm.Type // equivalent to uintptr in Go
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
byteOrder binary.ByteOrder // big-endian or little-endian
debug bool // log debug messages
pkgName string // package name of the currently executing package
functionCache map[llvm.Value]*function // cache of compiled functions
objects []object // slice of objects in memory
globals map[llvm.Value]int // map from global to index in objects slice
start time.Time
timeout time.Duration
callsExecuted uint64
mod llvm.Module
targetData llvm.TargetData
builder llvm.Builder
pointerSize uint32 // cached pointer size from the TargetData
dataPtrType llvm.Type // often used type so created in advance
uintptrType llvm.Type // equivalent to uintptr in Go
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
byteOrder binary.ByteOrder // big-endian or little-endian
debug bool // log debug messages
pkgName string // package name of the currently executing package
functionCache map[llvm.Value]*function // cache of compiled functions
objects []object // slice of objects in memory
globals map[llvm.Value]int // map from global to index in objects slice
start time.Time
timeout time.Duration
maxLoopIterations int
callsExecuted uint64
interpErr error // set by Uint/Int when they encounter pointer data
}
func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
func newRunner(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bool) *runner {
r := runner{
mod: mod,
targetData: llvm.NewTargetData(mod.DataLayout()),
byteOrder: llvmutil.ByteOrder(mod.Target()),
debug: debug,
functionCache: make(map[llvm.Value]*function),
objects: []object{{}},
globals: make(map[llvm.Value]int),
start: time.Now(),
timeout: timeout,
mod: mod,
targetData: llvm.NewTargetData(mod.DataLayout()),
byteOrder: llvmutil.ByteOrder(mod.Target()),
debug: debug,
functionCache: make(map[llvm.Value]*function),
objects: []object{{}},
globals: make(map[llvm.Value]int),
start: time.Now(),
timeout: timeout,
maxLoopIterations: maxLoopIterations,
}
r.pointerSize = uint32(r.targetData.PointerSize())
r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0)
@@ -64,8 +67,8 @@ func (r *runner) dispose() {
// Run evaluates runtime.initAll function as much as possible at compile time.
// Set debug to true if it should print output while running.
func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
r := newRunner(mod, timeout, debug)
func Run(mod llvm.Module, timeout time.Duration, maxLoopIterations int, debug bool) error {
r := newRunner(mod, timeout, maxLoopIterations, debug)
defer r.dispose()
initAll := mod.NamedFunction("runtime.initAll")
@@ -204,10 +207,10 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
// RunFunc evaluates a single package initializer at compile time.
// Set debug to true if it should print output while running.
func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
func RunFunc(fn llvm.Value, timeout time.Duration, maxLoopIterations int, debug bool) error {
// Create and initialize *runner object.
mod := fn.GlobalParent()
r := newRunner(mod, timeout, debug)
r := newRunner(mod, timeout, maxLoopIterations, debug)
defer r.dispose()
initName := fn.Name()
if !strings.HasSuffix(initName, ".init") {
+1 -1
View File
@@ -44,7 +44,7 @@ func runTest(t *testing.T, pathPrefix string) {
defer mod.Dispose()
// Perform the transform.
err = Run(mod, 10*time.Minute, false)
err = Run(mod, 10*time.Minute, DefaultMaxInterpBlockEntries, false)
if err != nil {
if err, match := err.(*Error); match {
println(err.Error())
+31 -1
View File
@@ -12,6 +12,12 @@ import (
"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) {
mem := memoryView{r: r, parent: parentMem}
locals := make([]value, len(fn.locals))
@@ -26,6 +32,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// This is used to prevent unrolling.
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.
// Branch instructions may modify both bb and instIndex when branching.
bb := fn.blocks[0]
@@ -36,6 +46,18 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
if instIndex == 0 {
// 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 _, ok := runtimeBlocks[lastBB]; ok {
// This loop has been unrolled.
@@ -277,7 +299,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// means that monotonic time in the time package is counted from
// time.Time{}.Sub(1), which should be fine.
locals[inst.localIndex] = literalValue{uint64(0)}
case callFn.name == "runtime.alloc":
case callFn.name == "runtime.alloc" || callFn.name == "runtime.alloc_noheap":
// Allocate heap memory. At compile time, this is instead done
// by creating a global variable.
@@ -825,6 +847,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
}
return nil, mem, r.errorAt(inst, errUnsupportedInst)
}
// 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"))
}
+11 -5
View File
@@ -556,11 +556,13 @@ func (v pointerValue) asRawValue(r *runner) rawValue {
}
func (v pointerValue) Uint(r *runner) uint64 {
panic("cannot convert pointer to integer")
r.interpErr = errUnsupportedInst
return 0
}
func (v pointerValue) Int(r *runner) int64 {
panic("cannot convert pointer to integer")
r.interpErr = errUnsupportedInst
return 0
}
func (v pointerValue) equal(rhs pointerValue) bool {
@@ -736,11 +738,12 @@ func (v rawValue) asRawValue(r *runner) rawValue {
return v
}
func (v rawValue) bytes() []byte {
func (v rawValue) bytes(r *runner) []byte {
buf := make([]byte, len(v.buf))
for i, p := range v.buf {
if p > 255 {
panic("cannot convert pointer value to byte")
r.interpErr = errUnsupportedInst
return buf
}
buf[i] = byte(p)
}
@@ -748,7 +751,10 @@ func (v rawValue) bytes() []byte {
}
func (v rawValue) Uint(r *runner) uint64 {
buf := v.bytes()
buf := v.bytes(r)
if r.interpErr != nil {
return 0
}
switch len(v.buf) {
case 1:
+1 -1
View File
@@ -245,7 +245,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"internal/cm/": false,
"internal/futex/": false,
"internal/fuzz/": false,
"internal/itoa": false,
"internal/itoa/": false,
"internal/reflectlite/": false,
"internal/gclayout": false,
"internal/task/": false,
+23
View File
@@ -304,6 +304,29 @@ func (p *Program) Sorted() []*Package {
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
// package of the program.
func (p *Program) MainPkg() *Package {
+3 -1
View File
@@ -8,7 +8,9 @@ import (
//
// The program must already be parsed and type-checked with the .Parse() method.
func (p *Program) LoadSSA() *ssa.Program {
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
// TODO: re-enable SanityCheckFunctions when x/tools is upgraded to
// 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 {
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
+40 -37
View File
@@ -30,6 +30,7 @@ import (
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/diagnostics"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/buildutil"
"tinygo.org/x/espflasher/pkg/espflasher"
@@ -588,7 +589,7 @@ func Flash(pkgName, port, outpath string, options *compileopts.Options) error {
return fmt.Errorf("unknown flash method: %s", flashMethod)
}
if options.Monitor {
return Monitor(result.Executable, "", config)
return Monitor(result.Executable, port, config)
}
return nil
}
@@ -1738,6 +1739,7 @@ func main() {
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")
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
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
target := flag.String("target", "", "chip/board name or JSON target specification file")
@@ -1855,42 +1857,43 @@ func main() {
}
options := &compileopts.Options{
GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
GOMIPS: goenv.Get("GOMIPS"),
Target: *target,
BuildMode: *buildMode,
StackSize: stackSize,
Opt: *opt,
GC: *gc,
PanicStrategy: *panicStrategy,
Scheduler: *scheduler,
Serial: *serial,
Work: *work,
InterpTimeout: *interpTimeout,
PrintIR: *printIR,
DumpSSA: *dumpSSA,
VerifyIR: *verifyIR,
SkipDWARF: *skipDwarf,
Semaphore: make(chan struct{}, *parallelism),
Debug: !*nodebug,
Nobounds: *nobounds,
PrintSizes: *printSize,
PrintStacks: *printStacks,
PrintAllocs: printAllocs,
Tags: []string(tags),
TestConfig: testConfig,
GlobalValues: globalVarValues,
Programmer: *programmer,
OpenOCDCommands: ocdCommands,
LLVMFeatures: *llvmFeatures,
Monitor: *monitor,
BaudRate: *baudrate,
Timeout: *timeout,
WITPackage: witPackage,
WITWorld: witWorld,
GoCompatibility: *gocompatibility,
GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
GOMIPS: goenv.Get("GOMIPS"),
Target: *target,
BuildMode: *buildMode,
StackSize: stackSize,
Opt: *opt,
GC: *gc,
PanicStrategy: *panicStrategy,
Scheduler: *scheduler,
Serial: *serial,
Work: *work,
InterpTimeout: *interpTimeout,
InterpMaxLoopIterations: *interpLoopLimit,
PrintIR: *printIR,
DumpSSA: *dumpSSA,
VerifyIR: *verifyIR,
SkipDWARF: *skipDwarf,
Semaphore: make(chan struct{}, *parallelism),
Debug: !*nodebug,
Nobounds: *nobounds,
PrintSizes: *printSize,
PrintStacks: *printStacks,
PrintAllocs: printAllocs,
Tags: []string(tags),
TestConfig: testConfig,
GlobalValues: globalVarValues,
Programmer: *programmer,
OpenOCDCommands: ocdCommands,
LLVMFeatures: *llvmFeatures,
Monitor: *monitor,
BaudRate: *baudrate,
Timeout: *timeout,
WITPackage: witPackage,
WITWorld: witWorld,
GoCompatibility: *gocompatibility,
}
if *printCommands {
options.PrintCommands = printCommand
+19
View File
@@ -77,6 +77,7 @@ func TestBuild(t *testing.T) {
"interface.go",
"json.go",
"map.go",
"map_bigkey.go",
"math.go",
"oldgo/",
"print.go",
@@ -155,6 +156,19 @@ func TestBuild(t *testing.T) {
}
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() {
@@ -283,6 +297,11 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
// limited amount of memory.
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":
// Does not pass due to high mark false positive rate.
continue
+2 -8
View File
@@ -453,10 +453,7 @@ func rawStructFieldFromPointer(descriptor *structType, fieldType *RawType, data
data = unsafe.Add(data, 1) // C: data+1
tagLen := uintptr(*(*byte)(data))
data = unsafe.Add(data, 1) // C: data+1
tag = *(*string)(unsafe.Pointer(&stringHeader{
data: data,
len: tagLen,
}))
tag = unsafe.String((*byte)(data), tagLen)
}
// Set the PkgPath to some (arbitrary) value if the package path is not
@@ -930,10 +927,7 @@ func readStringZ(data unsafe.Pointer) string {
data = unsafe.Add(data, 1) // C: data++
}
return *(*string)(unsafe.Pointer(&stringHeader{
data: start,
len: len,
}))
return unsafe.String((*byte)(start), len)
}
func (t *RawType) name() string {
+98 -88
View File
@@ -146,6 +146,19 @@ func TypeAssert[T any](v Value) (T, bool) {
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
// 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{} {
if v.typecode.Kind() == Interface {
// The value itself is an interface. This can happen when getting the
@@ -158,11 +171,7 @@ func valueInterfaceUnsafe(v Value) interface{} {
if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
// Value was indirect but must be put back directly in the interface
// value.
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)
v.value = loadSmallValue(v.value, v.typecode.Size())
}
return composeInterface(unsafe.Pointer(v.typecode), v.value)
}
@@ -713,20 +722,12 @@ func (v Value) Slice(i, j int) Value {
case String:
i, j := uintptr(i), uintptr(j)
str := *(*stringHeader)(v.value)
if j < i || str.len < j {
slicePanic()
}
hdr := stringHeader{
data: unsafe.Add(str.data, i),
len: j - i,
}
str := *(*string)(v.value)
sliced := str[i:j]
return Value{
typecode: v.typecode,
value: unsafe.Pointer(&hdr),
value: unsafe.Pointer(&sliced),
flags: v.flags,
}
}
@@ -801,7 +802,7 @@ func (v Value) Len() int {
case Slice:
return int((*sliceHeader)(v.value).len)
case String:
return int((*stringHeader)(v.value).len)
return len(*(*string)(v.value))
default:
panic(&ValueError{Method: "Len", Kind: v.Kind()})
}
@@ -977,13 +978,10 @@ func (v Value) Index(i int) Value {
// Keeping valueFlagExported if set, but don't set valueFlagIndirect
// otherwise CanSet will return true for string elements (which is bad,
// strings are read-only).
s := *(*stringHeader)(v.value)
if uint(i) >= uint(s.len) {
panic("reflect: string index out of range")
}
s := *(*string)(v.value)
return Value{
typecode: uint8Type,
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Add(s.data, i)))),
value: unsafe.Pointer(uintptr(s[i])),
flags: v.flags & valueFlagExported,
}
case Array:
@@ -1085,18 +1083,8 @@ func (v Value) MapKeys() []Value {
k := New(v.typecode.Key())
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) {
if shouldUnpackInterface {
intf := *(*interface{})(k.value)
v := ValueOf(intf)
keys = append(keys, v)
} else {
keys = append(keys, k.Elem())
}
keys = append(keys, k.Elem())
k = New(v.typecode.Key())
}
@@ -1109,9 +1097,40 @@ func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueS
//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet
func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
//go:linkname hashmapInterfaceGet runtime.hashmapInterfaceGet
func hashmapInterfaceGet(m unsafe.Pointer, key interface{}, value unsafe.Pointer, valueSize uintptr) bool
//go:linkname hashmapGenericGet runtime.hashmapGenericGet
func hashmapGenericGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
// genericKeyPtr returns a pointer to key data suitable for passing to the
// hashmapGeneric* functions. When the map's key type is an interface,
// special handling is needed: if the key Value already holds an interface
// (e.g. from MapKeys iteration), its memory already contains the
// {typecode, data} pair the hashmap expects, so we use it directly.
// If the key is a concrete type being assigned to an interface-keyed map,
// we compose the interface first.
func genericKeyPtr(vkey *RawType, key Value) unsafe.Pointer {
if vkey.Kind() == Interface {
if key.Kind() == Interface {
// Key is already an interface value stored indirectly;
// key.value points to {typecode, data}.
return key.value
}
// Concrete value being used as an interface key.
// For small addressable values, key.value is a pointer to
// the data, but the interface value field stores the data
// directly; load it using the same endian-safe approach as
// valueInterfaceUnsafe.
val := key.value
if key.isIndirect() && key.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
val = loadSmallValue(key.value, key.typecode.Size())
}
intf := composeInterface(unsafe.Pointer(key.typecode), val)
return unsafe.Pointer(&intf)
}
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
return key.value
}
return unsafe.Pointer(&key.value)
}
func (v Value) MapIndex(key Value) Value {
if v.Kind() != Map {
panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
@@ -1140,13 +1159,16 @@ func (v Value) MapIndex(key Value) Value {
} else {
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 {
return Value{}
}
return elem.Elem()
} else {
if ok := hashmapInterfaceGet(v.pointer(), key.Interface(), elem.value, elemType.Size()); !ok {
// Compiler-generated hash/equal path: keys are stored at their
// actual type. Use hashmapGenericGet which dispatches through the
// map's own keyHash/keyEqual function pointers.
keyptr := genericKeyPtr(vkey, key)
if ok := hashmapGenericGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
return Value{}
}
return elem.Elem()
@@ -1171,8 +1193,7 @@ type MapIter struct {
key Value
val Value
valid bool
unpackKeyInterface bool
valid bool
}
func (it *MapIter) Key() Value {
@@ -1180,12 +1201,6 @@ func (it *MapIter) Key() Value {
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()
}
@@ -1218,15 +1233,9 @@ func (iter *MapIter) Reset(v Value) {
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{
m: v,
it: hashmapNewIterator(),
unpackKeyInterface: shouldUnpackInterface,
m: v,
it: hashmapNewIterator(),
}
}
@@ -1714,8 +1723,7 @@ const zerobufferLen = 32
func init() {
// 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"
s := (*stringHeader)(unsafe.Pointer(&zerobufferStr))
zerobuffer = s.data
zerobuffer = unsafe.Pointer(unsafe.StringData(zerobufferStr))
}
func Zero(typ Type) Value {
@@ -1766,19 +1774,11 @@ type sliceHeader struct {
cap uintptr
}
// 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.
// Verify SliceHeader size.
// See https://github.com/tinygo-org/tinygo/pull/4156
// and https://github.com/tinygo-org/tinygo/issues/1284.
var (
_ [unsafe.Sizeof([]byte{})]byte = [unsafe.Sizeof(sliceHeader{})]byte{}
_ [unsafe.Sizeof("")]byte = [unsafe.Sizeof(stringHeader{})]byte{}
)
type ValueError struct {
@@ -1845,29 +1845,29 @@ func Copy(dst, src Value) int {
func buflen(v Value) (unsafe.Pointer, uintptr) {
var buf unsafe.Pointer
var len uintptr
var length uintptr
switch v.typecode.Kind() {
case Slice:
hdr := (*sliceHeader)(v.value)
buf = hdr.data
len = hdr.len
length = hdr.len
case Array:
if v.isIndirect() || v.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
buf = v.value
} else {
buf = unsafe.Pointer(&v.value)
}
len = uintptr(v.Len())
length = uintptr(v.Len())
case String:
hdr := (*stringHeader)(v.value)
buf = hdr.data
len = hdr.len
s := *(*string)(v.value)
buf = unsafe.Pointer(unsafe.StringData(s))
length = uintptr(len(s))
default:
// This shouldn't happen
panic("reflect.Copy: not slice or array or string")
}
return buf, len
return buf, length
}
//go:linkname sliceGrow runtime.sliceGrow
@@ -1969,8 +1969,8 @@ func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer)
//go:linkname hashmapBinarySet runtime.hashmapBinarySet
func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer)
//go:linkname hashmapInterfaceSet runtime.hashmapInterfaceSet
func hashmapInterfaceSet(m unsafe.Pointer, key interface{}, value unsafe.Pointer)
//go:linkname hashmapGenericSet runtime.hashmapGenericSet
func hashmapGenericSet(m unsafe.Pointer, key, value unsafe.Pointer)
//go:linkname hashmapStringDelete runtime.hashmapStringDelete
func hashmapStringDelete(m unsafe.Pointer, key string)
@@ -1978,8 +1978,8 @@ func hashmapStringDelete(m unsafe.Pointer, key string)
//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDelete
func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer)
//go:linkname hashmapInterfaceDelete runtime.hashmapInterfaceDelete
func hashmapInterfaceDelete(m unsafe.Pointer, key interface{})
//go:linkname hashmapGenericDelete runtime.hashmapGenericDelete
func hashmapGenericDelete(m unsafe.Pointer, key unsafe.Pointer)
func (v Value) SetMapIndex(key, elem Value) {
v.checkRO()
@@ -2002,15 +2002,19 @@ func (v Value) SetMapIndex(key, elem Value) {
}
// make elem an interface if it needs to be converted
if v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
intf := composeInterface(unsafe.Pointer(elem.typecode), elem.value)
if !del && v.typecode.elem().Kind() == Interface && elem.typecode.Kind() != Interface {
val := elem.value
if elem.isIndirect() && elem.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
val = loadSmallValue(elem.value, elem.typecode.Size())
}
intf := composeInterface(unsafe.Pointer(elem.typecode), val)
elem = Value{
typecode: v.typecode.elem(),
value: unsafe.Pointer(&intf),
}
}
if key.Kind() == String {
if vkey.Kind() == String {
if del {
hashmapStringDelete(v.pointer(), *(*string)(key.value))
} else {
@@ -2023,7 +2027,7 @@ func (v Value) SetMapIndex(key, elem Value) {
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
}
} else if key.typecode.isBinary() {
} else if vkey.isBinary() {
var keyptr unsafe.Pointer
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
keyptr = key.value
@@ -2043,8 +2047,11 @@ func (v Value) SetMapIndex(key, elem Value) {
hashmapBinarySet(v.pointer(), keyptr, elemptr)
}
} else {
// Compiler-generated hash/equal path.
keyptr := genericKeyPtr(vkey, key)
if del {
hashmapInterfaceDelete(v.pointer(), key.Interface())
hashmapGenericDelete(v.pointer(), keyptr)
} else {
var elemptr unsafe.Pointer
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
@@ -2053,7 +2060,7 @@ func (v Value) SetMapIndex(key, elem Value) {
elemptr = unsafe.Pointer(&elem.value)
}
hashmapInterfaceSet(v.pointer(), key.Interface(), elemptr)
hashmapGenericSet(v.pointer(), keyptr, elemptr)
}
}
}
@@ -2110,6 +2117,9 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value {
//go:linkname hashmapMake runtime.hashmapMake
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
// for approximately n elements.
func MakeMapWithSize(typ Type, n int) Value {
@@ -2118,7 +2128,6 @@ func MakeMapWithSize(typ Type, n int) Value {
const (
hashmapAlgorithmBinary uint8 = iota
hashmapAlgorithmString
hashmapAlgorithmInterface
)
if typ.Kind() != Map {
@@ -2132,18 +2141,19 @@ func MakeMapWithSize(typ Type, n int) Value {
key := typ.Key().(*RawType)
val := typ.Elem().(*RawType)
var alg uint8
var m unsafe.Pointer
if key.Kind() == String {
alg = hashmapAlgorithmString
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmString)
} else if key.isBinary() {
alg = hashmapAlgorithmBinary
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmBinary)
} else {
alg = hashmapAlgorithmInterface
// Composite key type (struct with strings, floats, etc.).
// Use runtime-generated hash/equal closures that walk the
// type structure, matching the compiler-generated functions.
m = hashmapMakeReflect(key.Size(), val.Size(), uintptr(n), unsafe.Pointer(key))
}
m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg)
return Value{
typecode: typ.(*RawType),
value: m,
+99
View File
@@ -0,0 +1,99 @@
package task
import (
"runtime/interrupt"
"unsafe"
)
// A waiter waits for an interrupt to fire. Call Wait() from a goroutine to
// pause it, and call Resume() from an interrupt handler to resume the
// goroutine.
type Waiter struct {
waiting *Task
lock PMutex
}
var resumeTaskSentinel = (*Task)(unsafe.Pointer(uintptr(2)))
var workingTaskSentinel = (*Task)(unsafe.Pointer(uintptr(1)))
// Wait from a main goroutine until an interrupt calls Resume(). It will also
// return if the interrupt called Resume() before the Wait() call (to avoid a
// race condition).
func (w *Waiter) Wait() {
if interrupt.In() {
runtimePanic("Waiter: called Wait from interrupt")
}
mask := interrupt.Disable()
w.lock.Lock()
switch w.waiting {
case nil, workingTaskSentinel:
w.waiting = Current()
w.lock.Unlock()
interrupt.Restore(mask)
Pause()
case resumeTaskSentinel:
// Marked as 'resume now', can return immediately.
w.waiting = workingTaskSentinel
w.lock.Unlock()
interrupt.Restore(mask)
default:
w.lock.Unlock()
interrupt.Restore(mask)
runtimePanic("Waiter: task is waiting already")
}
}
// Resume a waiting goroutine from an interrupt handler. If there is a goroutine
// waiting, it will resume. If not, the next one that calls Wait() will
// immediately resume.
func (w *Waiter) Resume() {
if !interrupt.In() {
runtimePanic("Waiter: called Resume outside interrupt")
}
waiting := w.waiting
switch waiting {
case nil, workingTaskSentinel:
w.waiting = resumeTaskSentinel
case resumeTaskSentinel:
// Called Resume() twice in a row, this may indicate a bug at the caller
// site.
default:
// Schedule the given task to resume.
// If it's not yet paused, it will immediately resume on the next call
// to Pause().
w.waiting = workingTaskSentinel
scheduleTask(waiting)
}
}
// Return true if Done has not been called after a Resume call.
// This is typically used to indicate the task outside the interrupt is still
// being worked on.
func (w *Waiter) Working() bool {
switch w.waiting {
case workingTaskSentinel, resumeTaskSentinel:
return true
default: // nil, <*task.Task>
return false
}
}
// Done can be called outside interrupt context to indicate the task this waiter
// was waiting for is done, and Working() can return false. It is entirely
// optional, if not called Working will continue to return true until it is
// blocked in Wait() but the waiter will function normally otherwise.
func (w *Waiter) Done() {
if interrupt.In() {
runtimePanic("Waiter: called Done from interrupt")
}
mask := interrupt.Disable()
w.lock.Lock()
if w.waiting == workingTaskSentinel {
w.waiting = nil
}
w.lock.Unlock()
interrupt.Restore(mask)
}
+22 -22
View File
@@ -1,12 +1,12 @@
package msc
import (
"internal/task"
"machine"
"machine/usb"
"machine/usb/descriptor"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"time"
)
type mscState uint8
@@ -26,14 +26,14 @@ const (
var MSC *msc
type msc struct {
buf []byte // Buffer for incoming/outgoing data
blockCache []byte // Buffer for block read/write data
taskQueued bool // Flag to indicate if the buffer has a task queued
rxStalled bool // Flag to indicate if the RX endpoint is stalled
txStalled bool // Flag to indicate if the TX endpoint is stalled
maxPacketSize uint32 // Maximum packet size for the IN endpoint
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
buf []byte // Buffer for incoming/outgoing data
blockCache []byte // Buffer for block read/write data
taskWaiter task.Waiter // Waiter for events outside interrupt context
rxStalled bool // Flag to indicate if the RX endpoint is stalled
txStalled bool // Flag to indicate if the TX endpoint is stalled
maxPacketSize uint32 // Maximum packet size for the IN endpoint
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
cbw *CBW // Last received Command Block Wrapper
queuedBytes uint32 // Number of bytes queued for sending
@@ -120,21 +120,21 @@ func newMSC(dev machine.BlockDevice) *msc {
func (m *msc) processTasks() {
// Process tasks that cannot be done in an interrupt context
for {
if m.taskQueued {
cmd := m.cbw.SCSICmd()
switch cmd.CmdType() {
case scsi.CmdWrite:
m.scsiWrite(cmd, m.buf)
case scsi.CmdUnmap:
m.scsiUnmap(m.buf)
}
// Wait for the next task to arrive.
m.taskWaiter.Wait()
// Acknowledge the received data from the host
m.queuedBytes = 0
m.taskQueued = false
machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
cmd := m.cbw.SCSICmd()
switch cmd.CmdType() {
case scsi.CmdWrite:
m.scsiWrite(cmd, m.buf)
case scsi.CmdUnmap:
m.scsiUnmap(m.buf)
}
time.Sleep(100 * time.Microsecond)
// Acknowledge the received data from the host
m.queuedBytes = 0
m.taskWaiter.Done()
machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
}
}
+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.
if m.taskQueued {
if m.taskWaiter.Working() {
// If we already have a full task queue we can't accept this data
m.sendScsiError(csw.StatusFailed, scsi.SenseAbortedCommand, scsi.SenseCodeMsgReject)
return true
@@ -268,14 +268,14 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
case scsi.CmdWrite:
// 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) {
m.taskQueued = true
m.taskWaiter.Resume()
}
case scsi.CmdUnmap:
m.taskQueued = true
m.taskWaiter.Resume()
}
// Don't acknowledge the incoming data until we can process it.
return !m.taskQueued
return !m.taskWaiter.Working()
}
func (m *msc) sendScsiError(status csw.Status, key scsi.Sense, code scsi.SenseCode) {
+1 -1
Submodule src/net updated: e54965ed5e...1026408a38
+247 -68
View File
@@ -13,16 +13,27 @@ import (
// The underlying hashmap structure for Go.
type hashmap struct {
buckets unsafe.Pointer // pointer to array of buckets
seed uintptr
count uintptr
keySize uintptr // maybe this can store the key type as well? E.g. keysize == 5 means string?
valueSize uintptr
bucketBits uint8
keyEqual func(x, y unsafe.Pointer, n uintptr) bool
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
buckets unsafe.Pointer // pointer to array of buckets
seed uintptr
count uintptr
keySize uintptr
valueSize uintptr
keySlotSize uintptr // == keySize, or sizeof(ptr) if indirect
valueSlotSize uintptr // == valueSize, or sizeof(ptr) if indirect
bucketBits uint8
flags uint8
keyEqual func(x, y unsafe.Pointer, n uintptr) bool
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
}
const (
hashmapMaxKeySize = 128
hashmapMaxValueSize = 128
hashmapFlagIndirectKey = 1 << 0
hashmapFlagIndirectValue = 1 << 1
)
// A hashmap bucket. A bucket is a container of 8 key/value pairs: first the
// 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
@@ -34,6 +45,48 @@ type hashmapBucket struct {
// 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 {
buckets unsafe.Pointer // pointer to array of hashapBuckets
numBuckets uintptr // length of buckets array
@@ -66,20 +119,35 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm
bucketBits++
}
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + keySize*8 + valueSize*8
var flags uint8
keySlotSize := keySize
if keySize > hashmapMaxKeySize {
flags |= hashmapFlagIndirectKey
keySlotSize = unsafe.Sizeof(unsafe.Pointer(nil))
}
valueSlotSize := valueSize
if valueSize > hashmapMaxValueSize {
flags |= hashmapFlagIndirectValue
valueSlotSize = unsafe.Sizeof(unsafe.Pointer(nil))
}
bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
keyHash := hashmapKeyHashAlg(tinygo.HashmapAlgorithm(alg))
keyEqual := hashmapKeyEqualAlg(tinygo.HashmapAlgorithm(alg))
return &hashmap{
buckets: buckets,
seed: uintptr(fastrand()),
keySize: keySize,
valueSize: valueSize,
bucketBits: bucketBits,
keyEqual: keyEqual,
keyHash: keyHash,
buckets: buckets,
seed: uintptr(fastrand()),
keySize: keySize,
valueSize: valueSize,
keySlotSize: keySlotSize,
valueSlotSize: valueSlotSize,
bucketBits: bucketBits,
flags: flags,
keyEqual: keyEqual,
keyHash: keyHash,
}
}
@@ -118,8 +186,6 @@ func hashmapKeyEqualAlg(alg tinygo.HashmapAlgorithm) func(x, y unsafe.Pointer, n
return memequal
case tinygo.HashmapAlgorithmString:
return hashmapStringEqual
case tinygo.HashmapAlgorithmInterface:
return hashmapInterfaceEqual
default:
// compiler bug :(
return nil
@@ -132,8 +198,6 @@ func hashmapKeyHashAlg(alg tinygo.HashmapAlgorithm) func(key unsafe.Pointer, n,
return hash32
case tinygo.HashmapAlgorithmString:
return hashmapStringPtrHash
case tinygo.HashmapAlgorithmInterface:
return hashmapInterfacePtrHash
default:
// compiler bug :(
return nil
@@ -172,7 +236,7 @@ func hashmapLen(m *hashmap) int {
//go:inline
func hashmapBucketSize(m *hashmap) uintptr {
return unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
return hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*8
}
//go:inline
@@ -191,16 +255,14 @@ func hashmapBucketAddrForHash(m *hashmap, hash uint32) *hashmapBucket {
//go:inline
func hashmapSlotKey(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(slot)
slotKey := unsafe.Add(unsafe.Pointer(bucket), slotKeyOffset)
return slotKey
slotKeyOffset := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*uintptr(slot)
return unsafe.Add(unsafe.Pointer(bucket), slotKeyOffset)
}
//go:inline
func hashmapSlotValue(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer {
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(slot)
slotValue := unsafe.Add(unsafe.Pointer(bucket), slotValueOffset)
return slotValue
slotValueOffset := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*uintptr(slot)
return unsafe.Add(unsafe.Pointer(bucket), slotValueOffset)
}
// Set a specified key to a given value. Grow the map if necessary.
@@ -234,9 +296,9 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
}
if bucket.tophash[i] == tophash {
// Could be an existing key that's the same.
if m.keyEqual(key, slotKey, m.keySize) {
// found same key, replace it
memcpy(slotValue, value, m.valueSize)
if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) {
// found same key, replace the value
hashmapStoreValue(m, slotValue, value)
return
}
}
@@ -251,11 +313,45 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
return
}
m.count++
memcpy(emptySlotKey, key, m.keySize)
memcpy(emptySlotValue, value, m.valueSize)
hashmapStoreKey(m, emptySlotKey, key)
hashmapStoreValue(m, emptySlotValue, value)
*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
// value into the bucket, and returns a pointer to this bucket.
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
@@ -267,8 +363,8 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u
slotKey := hashmapSlotKey(m, bucket, 0)
slotValue := hashmapSlotValue(m, bucket, 0)
m.count++
memcpy(slotKey, key, m.keySize)
memcpy(slotValue, value, m.valueSize)
hashmapStoreKey(m, slotKey, key)
hashmapStoreValue(m, slotValue, value)
bucket.tophash[0] = tophash
return bucket
}
@@ -331,12 +427,12 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u
for bucket != nil {
for i := uint8(0); i < 8; i++ {
slotKey := hashmapSlotKey(m, bucket, i)
slotValue := hashmapSlotValue(m, bucket, i)
if bucket.tophash[i] == tophash {
// This could be the key we're looking for.
if m.keyEqual(key, slotKey, m.keySize) {
if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) {
// Found the key, copy it.
memcpy(value, slotValue, m.valueSize)
slotValue := hashmapSlotValue(m, bucket, i)
memcpy(value, hashmapSlotValueData(m, slotValue), m.valueSize)
return true
}
}
@@ -370,13 +466,15 @@ func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32) {
slotKey := hashmapSlotKey(m, bucket, i)
if bucket.tophash[i] == tophash {
// This could be the key we're looking for.
if m.keyEqual(key, slotKey, m.keySize) {
if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) {
// Found the key, delete it.
bucket.tophash[i] = 0
// Zero out the key and value so garbage collector doesn't pin the allocations.
memzero(slotKey, m.keySize)
// Zero out the slot so the GC won't pin the allocations.
keySlotSize := hashmapKeySlotSize(m)
memzero(slotKey, keySlotSize)
slotValue := hashmapSlotValue(m, bucket, i)
memzero(slotValue, m.valueSize)
valueSlotSize := hashmapValueSlotSize(m)
memzero(slotValue, valueSlotSize)
m.count--
return
}
@@ -438,13 +536,13 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
// Found a key.
slotKey := hashmapSlotKey(m, it.bucket, it.bucketIndex)
memcpy(key, slotKey, m.keySize)
memcpy(key, hashmapSlotKeyData(m, slotKey), m.keySize)
if it.buckets == m.buckets {
// Our view of the buckets is the same as the parent map.
// Just copy the value we have
slotValue := hashmapSlotValue(m, it.bucket, it.bucketIndex)
memcpy(value, slotValue, m.valueSize)
memcpy(value, hashmapSlotValueData(m, slotValue), m.valueSize)
it.bucketIndex++
} else {
it.bucketIndex++
@@ -491,6 +589,112 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
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).
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
@@ -626,28 +830,3 @@ func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) ui
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
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,6 +66,11 @@ func llvm_sponentry() unsafe.Pointer
//export strlen
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
func malloc(size uintptr) unsafe.Pointer
+31
View File
@@ -82,3 +82,34 @@ func (m *Map) Swap(key, value any) (previous any, loaded bool) {
m.m[key] = value
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)) {
return nil, getErrno()
}
return (*[1 << 30]byte)(addr)[:length:length], nil
return unsafe.Slice((*byte)(addr), length), nil
}
func Munmap(b []byte) (err error) {
-1
View File
@@ -13,5 +13,4 @@ type HashmapAlgorithm uint8
const (
HashmapAlgorithmBinary HashmapAlgorithm = iota
HashmapAlgorithmString
HashmapAlgorithmInterface
)
+39
View File
@@ -243,6 +243,9 @@
wasi_snapshot_preview1: {
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md
fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
iovs_ptr >>>= 0;
iovs_len >>>= 0;
nwritten_ptr >>>= 0;
let nwritten = 0;
if (fd == 1) {
for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
@@ -284,6 +287,8 @@
throw wasmExit;
},
random_get: (bufPtr, bufLen) => {
bufPtr >>>= 0;
bufLen >>>= 0;
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
return 0;
},
@@ -294,6 +299,13 @@
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)
"runtime.sleepTicks": (timeout) => {
// Do not sleep, only reactivate scheduler after the given timeout.
@@ -328,12 +340,15 @@
// func stringVal(value string) ref
"syscall/js.stringVal": (value_ptr, value_len) => {
value_ptr >>>= 0;
value_len >>>= 0;
const s = loadString(value_ptr, value_len);
return boxValue(s);
},
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
p_ptr >>>= 0;
p_len >>>= 0;
let prop = loadString(p_ptr, p_len);
let v = unboxValue(v_ref);
let result = Reflect.get(v, prop);
@@ -342,6 +357,8 @@
// func valueSet(v ref, p string, 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 p = loadString(p_ptr, p_len);
const x = unboxValue(x_ref);
@@ -350,6 +367,8 @@
// func valueDelete(v ref, p string)
"syscall/js.valueDelete": (v_ref, p_ptr, p_len) => {
p_ptr >>>= 0;
p_len >>>= 0;
const v = unboxValue(v_ref);
const p = loadString(p_ptr, p_len);
Reflect.deleteProperty(v, p);
@@ -367,6 +386,11 @@
// 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) => {
ret_addr >>>= 0;
m_ptr >>>= 0;
m_len >>>= 0;
args_ptr >>>= 0;
args_len >>>= 0;
const v = unboxValue(v_ref);
const name = loadString(m_ptr, m_len);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
@@ -382,6 +406,9 @@
// func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
ret_addr >>>= 0;
args_ptr >>>= 0;
args_len >>>= 0;
try {
const v = unboxValue(v_ref);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
@@ -395,6 +422,9 @@
// func valueNew(v ref, args []ref) (ref, bool)
"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 args = loadSliceOfValues(args_ptr, args_len, args_cap);
try {
@@ -413,6 +443,7 @@
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (ret_addr, v_ref) => {
ret_addr >>>= 0;
const s = String(unboxValue(v_ref));
const str = encoder.encode(s);
storeValue(ret_addr, str);
@@ -421,6 +452,8 @@
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
slice_ptr >>>= 0;
slice_len >>>= 0;
const str = unboxValue(v_ref);
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
},
@@ -432,6 +465,9 @@
// func copyBytesToGo(dst []byte, src ref) (int, bool)
"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 returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
@@ -451,6 +487,9 @@
// Originally copied from upstream Go project, then modified:
// 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) => {
ret_addr >>>= 0;
src_addr >>>= 0;
src_len >>>= 0;
let num_bytes_copied_addr = ret_addr;
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
+69
View File
@@ -0,0 +1,69 @@
package main
//go:noheap
func main() {
// This object is optimized away, and won't cause a linker failure.
var a int
add(&a)
// This object is not optimized away since its address is captured.
var b int
escape(&b)
// A simple defer won't cause a heap allocation.
defer func() {
println("once")
}()
// A defer in a loop does causes a heap allocation.
for range 3 {
defer func() {
println("many")
}()
}
// This slice should not be heap-allocated.
s1 := make([]int, 5)
sum(s1)
// But this slice is.
s2 := make([]int, 5)
escape(&s2[0])
// Allocated at compile time as a global, so won't escape at runtime.
escape(globalInt)
}
func add(n *int) {
*n++
}
func sum(slice []int) (result int) {
for n := range slice {
result += n
}
return result
}
var globalInt = globalNewInt()
var globalAssign *int
//go:noheap
func globalNewInt() *int {
return new(int)
}
func escape(n *int) {
// Do some stuff that will definitely force a heap allocation.
// (This might be optimized in the future, in which case we need to change
// the function again).
n2 := func(n *int) *int {
return n
}(n)
println(n2)
}
// ERROR: noheap.go:10: object allocated on the heap in //go:noheap function
// ERROR: noheap.go:20: object allocated on the heap in //go:noheap function
// ERROR: noheap.go:30: object allocated on the heap in //go:noheap function
+9
View File
@@ -0,0 +1,9 @@
package main
// This global has a default value. It should be overridable via
// -ldflags="-X main.someGlobal=value" just like an uninitialized global.
var someGlobal = "default"
func main() {
println("someGlobal:", someGlobal)
}
+1
View File
@@ -0,0 +1 @@
someGlobal: foobar
+1 -2
View File
@@ -1,7 +1,6 @@
package main
// These globals can be changed using -ldflags="-X main.someGlobal=value".
// At the moment, only globals without an initializer can be replaced this way.
// This global can be changed using -ldflags="-X main.someGlobal=value".
var someGlobal string
func main() {
+85
View File
@@ -29,6 +29,14 @@ var testMapArrayKey = map[ArrayKey]int{
}
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 {
s string
f float32
@@ -130,6 +138,10 @@ func main() {
mapgrow()
nestedarraymaps()
ptrmaps()
interfacerehash()
}
@@ -308,3 +320,76 @@ func interfacerehash() {
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,4 +80,15 @@ tested growing of a map
2
2
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
+63
View File
@@ -0,0 +1,63 @@
package main
// Test maps with keys and values larger than 128 bytes, which triggers
// indirect storage in the bucket (pointers instead of inline data).
//
// This is a separate file from map.go because the compiler generates many
// large stack temporaries for map operations on [256]byte keys, which
// overflows the goroutine stack on AVR (384 bytes). AVR skips this test.
type BigKey [256]byte
type BigValue [256]byte
func main() {
// Large key, small value.
m1 := make(map[BigKey]int)
var k1 BigKey
k1[0] = 1
k1[255] = 42
m1[k1] = 100
var k1same BigKey
k1same[0] = 1
k1same[255] = 42
var k1diff BigKey
k1diff[0] = 2
println("bigkey get:", m1[k1])
println("bigkey get same:", m1[k1same])
println("bigkey get diff:", m1[k1diff])
// Overwrite.
m1[k1] = 200
println("bigkey overwrite:", m1[k1])
// Small key, large value.
m2 := make(map[int]BigValue)
var v BigValue
v[0] = 7
v[255] = 99
m2[1] = v
got := m2[1]
println("bigval get:", got[0], got[255])
// Both large.
m3 := make(map[BigKey]BigValue)
m3[k1] = v
got3 := m3[k1]
println("bigboth get:", got3[0], got3[255])
// Delete.
delete(m3, k1)
got3 = m3[k1]
println("bigboth deleted:", got3[0])
// Iteration.
m1[k1diff] = 300
count := 0
for range m1 {
count++
}
println("bigkey len:", len(m1), "iterated:", count)
}
+8
View File
@@ -0,0 +1,8 @@
bigkey get: 100
bigkey get same: 100
bigkey get diff: 0
bigkey overwrite: 200
bigval get: 7 99
bigboth get: 7 99
bigboth deleted: 0
bigkey len: 2 iterated: 2
+140
View File
@@ -396,6 +396,24 @@ func main() {
}
}
}
// Test for issue #3794: reflect MapIter.Key() should return a value with
// interface kind for map[interface{}] keys, not the underlying concrete kind.
{
m := make(map[interface{}]int)
m[1] = 2
m["hello"] = 3
rv := reflect.ValueOf(m)
iter := rv.MapRange()
for iter.Next() {
k := iter.Key()
if k.Kind() != reflect.Interface {
println("FAIL #3794: expected interface kind, got", k.Kind().String())
break
}
}
println("reflect map interface key ok")
}
}
func emptyFunc() {
@@ -755,6 +773,9 @@ func testImplements() {
// Make FooNode and BarNode implement Node with pointer receivers
// (can't add methods to local types in function, use a different approach)
testValueSetInterface()
testMakeMapCompositeKey()
testMakeMapInterfaceKey()
testMakeMapPaddedKey()
}
type IfaceNode interface {
@@ -807,3 +828,122 @@ func randuint32() uint32 {
xorshift32State = xorshift32(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,6 +503,24 @@ value set interface:
Set[0] to BarNode: 10
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:
struct{[0]func(); byte}: true
@@ -512,3 +530,4 @@ blue gopher
v.Interface() method
kind: interface
int 5
reflect map interface key ok
+98
View File
@@ -0,0 +1,98 @@
package mapbench
import "testing"
type compositeKey struct {
S string
N int32
}
var intSink int
func BenchmarkMapStringShortGet(b *testing.B) {
m := make(map[string]int, 100)
for i := 0; i < 100; i++ {
m[string(rune('A'+i%26))+string(rune('a'+i/26))] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
intSink += m["Qa"]
}
}
func BenchmarkMapStringLongGet(b *testing.B) {
m := make(map[string]int, 100)
for i := 0; i < 100; i++ {
s := "this-is-a-longer-key-for-testing-"
for j := 0; j < 3; j++ {
s += string(rune('A' + (i+j)%26))
}
m[s] = i
}
key := "this-is-a-longer-key-for-testing-ABC"
b.ResetTimer()
for i := 0; i < b.N; i++ {
intSink += m[key]
}
}
func BenchmarkMapCompositeGet(b *testing.B) {
m := make(map[compositeKey]int, 100)
for i := 0; i < 100; i++ {
m[compositeKey{S: string(rune('A'+i%26)) + string(rune('a'+i/26)), N: int32(i)}] = i
}
key := compositeKey{S: "Qa", N: 42}
b.ResetTimer()
for i := 0; i < b.N; i++ {
intSink += m[key]
}
}
func BenchmarkMapIntGet(b *testing.B) {
m := make(map[int]int, 100)
for i := 0; i < 100; i++ {
m[i*7] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
intSink += m[42]
}
}
func BenchmarkMapCompositeSet(b *testing.B) {
m := make(map[compositeKey]int, b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
m[compositeKey{S: "key", N: int32(i)}] = i
}
}
type bigKey [256]byte
func BenchmarkMapBigKeyGet(b *testing.B) {
m := make(map[bigKey]int, 100)
for i := 0; i < 100; i++ {
var k bigKey
k[0] = byte(i)
m[k] = i
}
var k bigKey
k[0] = 42
b.ResetTimer()
for i := 0; i < b.N; i++ {
intSink += m[k]
}
}
func BenchmarkMapBigKeySet(b *testing.B) {
m := make(map[bigKey]int, b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
var k bigKey
k[0] = byte(i)
k[1] = byte(i >> 8)
k[2] = byte(i >> 16)
k[3] = byte(i >> 24)
m[k] = i
}
}
+16 -3
View File
@@ -23,8 +23,15 @@ import (
// heap allocation explanation should be printed (why the object can't be stack
// allocated).
func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc uint64, logger func(token.Position, string)) {
allocator := mod.NamedFunction("runtime.alloc")
if allocator.IsNil() {
// Find allocator functions.
var allocators []llvm.Value
for _, name := range []string{"runtime.alloc", "runtime.alloc_noheap"} {
allocator := mod.NamedFunction(name)
if !allocator.IsNil() {
allocators = append(allocators, allocator)
}
}
if len(allocators) == 0 {
// nothing to optimize
return
}
@@ -43,7 +50,13 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
fmt.Fprintln(os.Stderr, "mode: set")
}
for _, heapalloc := range getUses(allocator) {
// Find all allocator calls.
var heapallocs []llvm.Value
for _, allocator := range allocators {
heapallocs = append(heapallocs, getUses(allocator)...)
}
for _, heapalloc := range heapallocs {
logAllocs := printAllocs != nil && printAllocs.MatchString(heapalloc.InstructionParent().Parent().Name())
if heapalloc.Operand(0).IsAConstantInt().IsNil() {
// Do not allocate variable length arrays on the stack.
+28 -22
View File
@@ -10,38 +10,44 @@ import (
// maps. This has not yet been implemented, however.
func OptimizeMaps(mod llvm.Module) {
hashmapMake := mod.NamedFunction("runtime.hashmapMake")
if hashmapMake.IsNil() {
// nothing to optimize
return
}
hashmapMakeGeneric := mod.NamedFunction("runtime.hashmapMakeGeneric")
hashmapBinarySet := mod.NamedFunction("runtime.hashmapBinarySet")
hashmapStringSet := mod.NamedFunction("runtime.hashmapStringSet")
hashmapGenericSet := mod.NamedFunction("runtime.hashmapGenericSet")
for _, makeInst := range getUses(hashmapMake) {
updateInsts := []llvm.Value{}
unknownUses := false // are there any uses other than setting a value?
optimizeMapMake := func(makeFunc llvm.Value) {
if makeFunc.IsNil() {
return
}
for _, makeInst := range getUses(makeFunc) {
updateInsts := []llvm.Value{}
unknownUses := false
for _, use := range getUses(makeInst) {
if use := use.IsACallInst(); !use.IsNil() {
switch use.CalledValue() {
case hashmapBinarySet, hashmapStringSet:
updateInsts = append(updateInsts, use)
default:
for _, use := range getUses(makeInst) {
if use := use.IsACallInst(); !use.IsNil() {
switch use.CalledValue() {
case hashmapBinarySet, hashmapStringSet, hashmapGenericSet:
updateInsts = append(updateInsts, use)
default:
unknownUses = true
}
} else {
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()
if !unknownUses {
// This map can be entirely removed, as it is only created
// but never used.
for _, inst := range updateInsts {
inst.EraseFromParentAsInstruction()
}
makeInst.EraseFromParentAsInstruction()
}
makeInst.EraseFromParentAsInstruction()
}
}
optimizeMapMake(hashmapMake)
optimizeMapMake(hashmapMakeGeneric)
}