compiler: implement clear builtin for slices
Этот коммит содержится в:
родитель
f791c821ff
коммит
a2f886a67a
6 изменённых файлов: 95 добавлений и 21 удалений
|
@ -1600,6 +1600,41 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
case "clear":
|
||||
value := argValues[0]
|
||||
switch typ := argTypes[0].Underlying().(type) {
|
||||
case *types.Slice:
|
||||
elementType := b.getLLVMType(typ.Elem())
|
||||
elementSize := b.targetData.TypeAllocSize(elementType)
|
||||
elementAlign := b.targetData.ABITypeAlignment(elementType)
|
||||
|
||||
// The pointer to the data to be cleared.
|
||||
llvmBuf := b.CreateExtractValue(value, 0, "buf")
|
||||
if llvmBuf.Type() != b.i8ptrType { // compatibility with LLVM 14
|
||||
llvmBuf = b.CreateBitCast(llvmBuf, b.i8ptrType, "")
|
||||
}
|
||||
|
||||
// The length (in bytes) to be cleared.
|
||||
llvmLen := b.CreateExtractValue(value, 1, "len")
|
||||
llvmLen = b.CreateMul(llvmLen, llvm.ConstInt(llvmLen.Type(), elementSize, false), "")
|
||||
|
||||
// Do the clear operation using the LLVM memset builtin.
|
||||
// This is also correct for nil slices: in those cases, len will be
|
||||
// 0 which means the memset call is a no-op (according to the LLVM
|
||||
// LangRef).
|
||||
memset := b.getMemsetFunc()
|
||||
call := b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
|
||||
llvmBuf, // dest
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false), // val
|
||||
llvmLen, // len
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false), // isVolatile
|
||||
}, "")
|
||||
call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))
|
||||
|
||||
return llvm.Value{}, nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())
|
||||
}
|
||||
case "copy":
|
||||
dst := argValues[0]
|
||||
src := argValues[1]
|
||||
|
|
|
@ -70,15 +70,7 @@ func (b *builder) createMemoryCopyImpl() {
|
|||
// regular libc memset calls if they aren't optimized out in a different way.
|
||||
func (b *builder) createMemoryZeroImpl() {
|
||||
b.createFunctionStart(true)
|
||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
llvmFn := b.getMemsetFunc()
|
||||
params := []llvm.Value{
|
||||
b.getValue(b.fn.Params[0], getPos(b.fn)),
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||
|
@ -89,6 +81,20 @@ func (b *builder) createMemoryZeroImpl() {
|
|||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// Return the llvm.memset.p0.i8 function declaration.
|
||||
func (c *compilerContext) getMemsetFunc() llvm.Value {
|
||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
|
||||
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
|
||||
}
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
|
||||
// using inline assembly.
|
||||
func (b *builder) createKeepAliveImpl() {
|
||||
|
|
8
compiler/testdata/go1.21.go
предоставленный
8
compiler/testdata/go1.21.go
предоставленный
|
@ -51,3 +51,11 @@ func maxFloat32(a, b float32) float32 {
|
|||
func maxString(a, b string) string {
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
func clearSlice(s []int) {
|
||||
clear(s)
|
||||
}
|
||||
|
||||
func clearZeroSizedSlice(s []struct{}) {
|
||||
clear(s)
|
||||
}
|
||||
|
|
42
compiler/testdata/go1.21.ll
предоставленный
42
compiler/testdata/go1.21.ll
предоставленный
|
@ -84,10 +84,10 @@ entry:
|
|||
%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) #4
|
||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = extractvalue %runtime._string %5, 0
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
|
@ -123,30 +123,48 @@ entry:
|
|||
%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) #4
|
||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
|
||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||
%6 = extractvalue %runtime._string %5, 0
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %5
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.smin.i32(i32, i32) #3
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = shl i32 %s.len, 2
|
||||
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly
|
||||
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 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i8 @llvm.umin.i8(i8, i8) #3
|
||||
declare i32 @llvm.smin.i32(i32, i32) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.umin.i32(i32, i32) #3
|
||||
declare i8 @llvm.umin.i8(i8, i8) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.smax.i32(i32, i32) #3
|
||||
declare i32 @llvm.umin.i32(i32, i32) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.umax.i32(i32, i32) #3
|
||||
declare i32 @llvm.smax.i32(i32, i32) #4
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
|
||||
declare i32 @llvm.umax.i32(i32, i32) #4
|
||||
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #3 = { nocallback nofree nosync nounwind readnone speculatable willreturn }
|
||||
attributes #4 = { nounwind }
|
||||
attributes #3 = { argmemonly nocallback nofree nounwind willreturn writeonly }
|
||||
attributes #4 = { nocallback nofree nosync nounwind readnone speculatable willreturn }
|
||||
attributes #5 = { nounwind }
|
||||
|
|
6
testdata/go1.21.go
предоставленный
6
testdata/go1.21.go
предоставленный
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
// The new min/max builtins.
|
||||
ia := 1
|
||||
ib := 5
|
||||
ic := -3
|
||||
|
@ -9,4 +10,9 @@ func main() {
|
|||
fc := -3.0
|
||||
println("min/max:", min(ia, ib, ic), max(ia, ib, ic))
|
||||
println("min/max:", min(fa, fb, fc), max(fa, fb, fc))
|
||||
|
||||
// The clear builtin, for slices.
|
||||
s := []int{1, 2, 3, 4, 5}
|
||||
clear(s[:3])
|
||||
println("cleared s[:3]:", s[0], s[1], s[2], s[3], s[4])
|
||||
}
|
||||
|
|
1
testdata/go1.21.txt
предоставленный
1
testdata/go1.21.txt
предоставленный
|
@ -1,2 +1,3 @@
|
|||
min/max: -3 5
|
||||
min/max: -3.000000e+000 +5.000000e+000
|
||||
cleared s[:3]: 0 0 0 4 5
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче