From cb5f7ad3fd881a94781e4832deb81294f8adc5af Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 21 May 2025 13:23:35 +0200 Subject: [PATCH] interp: fix copy() from/to external buffers This includes copying from a //go:embed slice for example. The slice with data is not available at interp time. See: https://github.com/tinygo-org/tinygo/issues/4895 --- interp/interpreter.go | 15 ++++++++++++++- interp/testdata/slice-copy.ll | 18 ++++++++++++++++++ interp/testdata/slice-copy.out.ll | 10 ++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/interp/interpreter.go b/interp/interpreter.go index 3813035e1..f6fb7bf3b 100644 --- a/interp/interpreter.go +++ b/interp/interpreter.go @@ -364,9 +364,22 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent continue } nBytes := uint32(n * elemSize) + srcObj := mem.get(src.index()) dstObj := mem.getWritable(dst.index()) + if srcObj.buffer == nil || dstObj.buffer == nil { + // If the buffer is nil, it means the slice is external. + // This can happen for example when copying data out of + // a //go:embed slice, which is not available at interp + // time. + // See: https://github.com/tinygo-org/tinygo/issues/4895 + err := r.runAtRuntime(fn, inst, locals, &mem, indent) + if err != nil { + return nil, mem, err + } + continue + } dstBuf := dstObj.buffer.asRawValue(r) - srcBuf := mem.get(src.index()).buffer.asRawValue(r) + srcBuf := srcObj.buffer.asRawValue(r) copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():]) dstObj.buffer = dstBuf mem.put(dst.index(), dstObj) diff --git a/interp/testdata/slice-copy.ll b/interp/testdata/slice-copy.ll index 5d741358a..e3e3dedfe 100644 --- a/interp/testdata/slice-copy.ll +++ b/interp/testdata/slice-copy.ll @@ -11,6 +11,10 @@ target triple = "x86_64--linux" @main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer @main.sliceSrcTaint.buf = internal global [2 x i8] c"cd" @main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer +@main.sliceSrcExternal1.buf = external global [2 x i8] +@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer +@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer +@main.sliceDstExternal2.buf = external global [2 x i8] declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr @@ -58,6 +62,14 @@ entry: %sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0) call void @runtime.printuint8(i8 %sliceDstTaint.val) + ; print(sliceDstExternal1[0]) + %sliceDstExternal1.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal1.buf, i32 0) + call void @runtime.printuint8(i8 %sliceDstExternal1.val) + + ; print(sliceDstExternal2[0]) + %sliceDstExternal2.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal2.buf, i32 0) + call void @runtime.printuint8(i8 %sliceDstExternal2.val) + ret void } @@ -102,5 +114,11 @@ entry: call void @use(ptr @main.sliceSrcTaint.buf) %copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1) + ; Test that copying from or into external buffers works correctly. + ; These copy operations must be done at runtime. + ; https://github.com/tinygo-org/tinygo/issues/4895 + %copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1) + %copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1) + ret void } diff --git a/interp/testdata/slice-copy.out.ll b/interp/testdata/slice-copy.out.ll index d61619e13..4c167e7d8 100644 --- a/interp/testdata/slice-copy.out.ll +++ b/interp/testdata/slice-copy.out.ll @@ -3,6 +3,10 @@ target triple = "x86_64--linux" @main.sliceSrcTaint.buf = internal global [2 x i8] c"cd" @main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer +@main.sliceSrcExternal1.buf = external global [2 x i8] +@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer +@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer +@main.sliceDstExternal2.buf = external global [2 x i8] declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr @@ -16,6 +20,8 @@ define void @runtime.initAll() unnamed_addr { entry: call void @use(ptr @main.sliceSrcTaint.buf) %copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1) + %copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1) + %copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1) ret void } @@ -28,5 +34,9 @@ entry: call void @runtime.printuint8(i8 97) %sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1 call void @runtime.printuint8(i8 %sliceDstTaint.val) + %sliceDstExternal1.val = load i8, ptr @main.sliceDstExternal1.buf, align 1 + call void @runtime.printuint8(i8 %sliceDstExternal1.val) + %sliceDstExternal2.val = load i8, ptr @main.sliceDstExternal2.buf, align 1 + call void @runtime.printuint8(i8 %sliceDstExternal2.val) ret void }