mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-09 05:23:40 +00:00
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.
This commit is contained in:
+31
-3
@@ -1682,13 +1682,41 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
case "copy":
|
||||
dst := argValues[0]
|
||||
src := argValues[1]
|
||||
// Fetch the lengths.
|
||||
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
||||
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
||||
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
||||
// Find the minimum of the lengths.
|
||||
minFuncName := "llvm.umin.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
minFunc := b.mod.NamedFunction(minFuncName)
|
||||
if minFunc.IsNil() {
|
||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType, b.uintptrType}, false)
|
||||
minFunc = llvm.AddFunction(b.mod, minFuncName, fnType)
|
||||
}
|
||||
minLen := b.CreateCall(minFunc.GlobalValueType(), minFunc, []llvm.Value{dstLen, srcLen}, "copy.n")
|
||||
// Multiply the length by the element size.
|
||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
||||
// NOTE: This is also NSW when uintptr is int, but we can only choose one through the C API?
|
||||
size := b.CreateNUWMul(minLen, elemSize, "copy.size")
|
||||
// Fetch the pointers.
|
||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstPtr")
|
||||
srcBuf := b.CreateExtractValue(src, 0, "copy.srcPtr")
|
||||
// Create a memcpy.
|
||||
call := b.createMemCopy("memmove", dstBuf, srcBuf, size)
|
||||
align := b.targetData.ABITypeAlignment(elemType)
|
||||
if align > 1 {
|
||||
// Apply the type's alignment to the arguments.
|
||||
// LLVM sometimes turns constant-length moves into loads and stores.
|
||||
// It may use this alignment for the created loads and stores.
|
||||
alignAttr := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align))
|
||||
call.AddCallSiteAttribute(1, alignAttr)
|
||||
call.AddCallSiteAttribute(2, alignAttr)
|
||||
}
|
||||
// Extend and return the copied length.
|
||||
if b.targetData.TypeAllocSize(minLen.Type()) < b.targetData.TypeAllocSize(b.intType) {
|
||||
minLen = b.CreateZExt(minLen, b.intType, "copy.n.zext")
|
||||
}
|
||||
return minLen, nil
|
||||
case "delete":
|
||||
m := argValues[0]
|
||||
key := argValues[1]
|
||||
|
||||
Reference in New Issue
Block a user