Files
tinygo/src/runtime/slice.go
T
Nia Waldvogel a40586d092 compiler: implement copy directly
The compiler now implements the copy builtin directly instead of calling sliceCopy.
The length is calculated with the llvm.umax.* intrinsics, and the move is performed by llvm.memmove.*.
Both of these operations are easily understood by LLVM's optimization passes.
The type's alignment is also provided to llvm.memmove.*, which is useful when rewriting the move.

Interp no longer needs to reimplement sliceCopy.
Some edge case handling was implemented by sliceCopy but not llvm.memmove.*/llvm.memcpy.*.
I copied this over, so copies of external slices should work now.
Volatile moves/copies are now run at runtime by interp.

There is a 4-byte size increase due to some confusing length logic in sendUSBPacket.
I will look at sendUSBPacket in a future PR.
2026-01-05 15:10:31 -05:00

47 lines
1.6 KiB
Go

package runtime
// This file implements compiler builtins for slices: append() and copy().
import (
"math/bits"
"unsafe"
)
// Builtin append(src, elements...) function: append elements to src and return
// the modified (possibly expanded) slice.
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr) {
newLen := srcLen + elemsLen
if elemsLen > 0 {
// Allocate a new slice with capacity for elemsLen more elements, if necessary;
// otherwise, reuse the passed slice.
srcBuf, _, srcCap = sliceGrow(srcBuf, srcLen, srcCap, newLen, elemSize, layout)
// Append the new elements in-place.
memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize)
}
return srcBuf, newLen, srcCap
}
// sliceGrow returns a new slice with space for at least newCap elements
func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr) {
if oldCap >= newCap {
// No need to grow, return the input slice.
return oldBuf, oldLen, oldCap
}
// This can be made more memory-efficient by multiplying by some other constant, such as 1.5,
// which seems to be allowed by the Go language specification (but this can be observed by
// programs); however, due to memory fragmentation and the current state of the TinyGo
// memory allocators, this causes some difficult to debug issues.
newCap = 1 << bits.Len(uint(newCap))
buf := alloc(newCap*elemSize, layout)
if oldLen > 0 {
// copy any data to new slice
memmove(buf, oldBuf, oldLen*elemSize)
}
return buf, oldLen, newCap
}