diff --git a/compiler.go b/compiler.go index 07270501..11ca3cac 100644 --- a/compiler.go +++ b/compiler.go @@ -1523,6 +1523,28 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string) default: return llvm.Value{}, errors.New("todo: cap: unknown type") } + case "copy": + dst, err := c.parseExpr(frame, args[0]) + if err != nil { + return llvm.Value{}, err + } + src, err := c.parseExpr(frame, args[1]) + if err != nil { + return llvm.Value{}, err + } + if _, ok := args[1].Type().(*types.Basic); ok { + return llvm.Value{}, errors.New("todo: copy: string to []byte") + } + dstLen := c.builder.CreateExtractValue(dst, 1, "copy.dstLen") + srcLen := c.builder.CreateExtractValue(src, 1, "copy.srcLen") + dstBuf := c.builder.CreateExtractValue(dst, 0, "copy.dstArray") + srcBuf := c.builder.CreateExtractValue(src, 0, "copy.srcArray") + elemType := dstBuf.Type().ElementType() + dstBuf = c.builder.CreateBitCast(dstBuf, c.i8ptrType, "copy.dstPtr") + srcBuf = c.builder.CreateBitCast(srcBuf, c.i8ptrType, "copy.srcPtr") + elemSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(elemType), false) + sliceCopy := c.mod.NamedFunction("runtime.sliceCopy") + return c.builder.CreateCall(sliceCopy, []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil case "len": value, err := c.parseExpr(frame, args[0]) if err != nil { diff --git a/src/examples/test/test.go b/src/examples/test/test.go index ec899559..537bc77d 100644 --- a/src/examples/test/test.go +++ b/src/examples/test/test.go @@ -37,11 +37,13 @@ func main() { // slice l := 5 foo := []int{1, 2, 4, 5} - bar := make([]byte, l-2, l) + bar := make([]int, l-2, l) println("len/cap foo:", len(foo), cap(foo)) println("len/cap bar:", len(bar), cap(bar)) println("foo[3]:", foo[3]) println("sum foo:", sum(foo)) + println("copy foo -> bar:", copy(bar, foo)) + println("sum bar:", sum(bar)) // interfaces, pointers thing := &Thing{"foo"} diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index e7c7f23b..c6e892d6 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -63,6 +63,25 @@ func memcpy(dst, src unsafe.Pointer, size uintptr) { } } +// Copy size bytes from src to dst. The memory areas may overlap and will do the +// correct thing. +func memmove(dst, src unsafe.Pointer, size uintptr) { + if uintptr(dst) < uintptr(src) { + // Copy forwards. + memcpy(dst, src, size) + return + } + // Copy backwards. + i := size + for { + i-- + *(*uint8)(unsafe.Pointer(uintptr(dst) + i)) = *(*uint8)(unsafe.Pointer(uintptr(src) + i)) + if i == 0 { + break + } + } +} + // Set the given number of bytes to zero. func memzero(ptr unsafe.Pointer, size uintptr) { for i := uintptr(0); i < size; i++ { @@ -81,3 +100,14 @@ func memequal(x, y unsafe.Pointer, n uintptr) bool { } return true } + +// Builtin copy(dst, src) function: copy bytes from dst to src. +func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen lenType, elemSize uintptr) lenType { + // n = min(srcLen, dstLen) + n := srcLen + if n > dstLen { + n = dstLen + } + memmove(dst, src, uintptr(n)*elemSize) + return n +}