diff --git a/transform/allocs.go b/transform/allocs.go index 5c1021a3fb..dfe6ec51df 100644 --- a/transform/allocs.go +++ b/transform/allocs.go @@ -52,12 +52,15 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u heapallocs = append(heapallocs, getUses(allocator)...) } + idMiner := makeIdMiner() for _, heapalloc := range heapallocs { logAllocs := printAllocs != nil && printAllocs.MatchString(heapalloc.InstructionParent().Parent().Name()) if heapalloc.Operand(0).IsAConstantInt().IsNil() { // Do not allocate variable length arrays on the stack. if logAllocs { - logger(getPosition(heapalloc), "size is not constant") + logger( + getPosition(heapalloc), + fmt.Sprintf("%s size is not constant", idMiner.get(heapalloc))) } continue } @@ -67,7 +70,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u // The maximum size for a stack allocation. if logAllocs { logger(getPosition(heapalloc), - fmt.Sprintf("object size %d exceeds maximum stack allocation size %d", size, maxStackAlloc)) + fmt.Sprintf("%s size %d exceeds maximum stack allocation size %d", idMiner.get(heapalloc), size, maxStackAlloc)) } continue } @@ -104,7 +107,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u if atPos.Line != 0 { msg = fmt.Sprintf("escapes at line %d", atPos.Line) } - logger(getPosition(heapalloc), msg) + logger(getPosition(heapalloc), fmt.Sprintf("%s %s", idMiner.get(heapalloc), msg)) } continue } @@ -316,3 +319,61 @@ func lineLengthAt(filename string, lineNumber int) int { } return 0 } + +type idMiner struct { + complitReg *regexp.Regexp +} + +func makeIdMiner() idMiner { + return idMiner{complitReg: regexp.MustCompile(`^complit\d*$`)} +} + +func (this *idMiner) get(v llvm.Value) string { + id := v.Name() + if len(id) == 0 { + if v.InstructionOpcode() == llvm.Call && (!v.IsAAllocaInst().IsNil() || !v.IsACallInst().IsNil() || + !v.IsAInvokeInst().IsNil()) { + id = v.AllocatedType().String() + } + } + if this.complitReg.MatchString(id) { + // Handle case like: c := scaleVector3(&vector3{4, 5, 6}, 0.5) + /* + %complit = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #1, !dbg !53 + %0 = getelementptr inbounds nuw i8, ptr %complit, i32 4, !dbg !53 + %1 = getelementptr inbounds nuw i8, ptr %complit, i32 8, !dbg !53 + store float 4.000000e+00, ptr %complit, align 4, !dbg !54 + store float 5.000000e+00, ptr %0, align 4, !dbg !55 + store float 6.000000e+00, ptr %1, align 4, !dbg !56 + %2 = call ptr @main.scaleVector3(ptr nonnull %complit, float 5.000000e-01, ptr undef), !dbg !57 + store ptr %2, ptr @main.escapedVector3, align 4, !dbg !60 + ret void, !dbg !61 + */ + /* + * 1. Find the next "call", where %complit is passed as parameter. + * 2. Produce ID: "Arg 0 of main.scaleVector3() call" // escapes at ... + */ + n := v + InstLoop: + for _ = range 64 { + n = llvm.NextInstruction(n) + if n.IsNil() { + break + } + if call := n.IsACallInst(); !call.IsNil() { + argIdx := 0 + for argIdx = range call.OperandsCount() { + argV := call.Operand(argIdx) + if argV.Name() == id { + id = fmt.Sprintf("Arg %d of %s() call", argIdx, call.CalledValue().Name()) + break InstLoop + } + } + } + } + } + if len(id) == 0 { + id = "allocation" + } + return id +} diff --git a/transform/allocs_test.go b/transform/allocs_test.go index 83ba90ffe8..794d717dde 100644 --- a/transform/allocs_test.go +++ b/transform/allocs_test.go @@ -60,8 +60,24 @@ func TestAllocs2(t *testing.T) { for i, line := range strings.Split(strings.ReplaceAll(string(testInput), "\r\n", "\n"), "\n") { const prefix = " // OUT: " if idx := strings.Index(line, prefix); idx > 0 { - msg := line[idx+len(prefix):] - fmt.Fprintf(&expectedTestOutput, "allocs2.go:%d: %s\n", i+1, msg) + msgLine := strings.TrimSpace(line[idx+len(prefix):]) + msgs := make([]string, 0, 2) + const separator = " && " + for true { + idx = strings.Index(msgLine, separator) + if idx < 0 { + break + } + msg := strings.TrimSpace(msgLine[:idx]) + msgs = append(msgs, msg) + msgLine = strings.TrimSpace(msgLine[idx+len(separator):]) + } + if len(msgLine) > 0 { + msgs = append(msgs, msgLine) + } + for _, msg := range msgs { + fmt.Fprintf(&expectedTestOutput, "allocs2.go:%d: %s\n", i+1, msg) + } } } diff --git a/transform/testdata/allocs2.go b/transform/testdata/allocs2.go index b8131d821e..1b2efd72ae 100644 --- a/transform/testdata/allocs2.go +++ b/transform/testdata/allocs2.go @@ -21,19 +21,19 @@ func main() { s3 := make([]int, 3) returnIntSlice(s3) - useSlice(make([]int, getUnknownNumber())) // OUT: size is not constant + useSlice(make([]int, getUnknownNumber())) // OUT: makeslice.buf size is not constant - s4 := make([]byte, 300) // OUT: object size 300 exceeds maximum stack allocation size 256 + s4 := make([]byte, 300) // OUT: makeslice4 size 300 exceeds maximum stack allocation size 256 readByteSlice(s4) - s5 := make([]int, 4) // OUT: escapes at line 30 + s5 := make([]int, 4) // OUT: makeslice6 escapes at line 30 _ = append(s5, 5) s6 := make([]int, 3) s7 := []int{1, 2, 3} copySlice(s6, s7) - c1 := getComplex128() // OUT: escapes at line 37 + c1 := getComplex128() // OUT: FloatType escapes at line 37 useInterface(c1) n3 := 5 @@ -41,13 +41,13 @@ func main() { return n3 }() - callVariadic(3, 5, 8) // OUT: escapes at line 44 + callVariadic(3, 5, 8) // OUT: varargs12 escapes at line 44 - s8 := []int{3, 5, 8} // OUT: escapes at line 47 + s8 := []int{3, 5, 8} // OUT: slicelit14 escapes at line 47 callVariadic(s8...) - n4 := 3 // OUT: escapes at line 51 - n5 := 7 // OUT: escapes at line 51 + n4 := 3 // OUT: n4 escapes at line 51 + n5 := 7 // OUT: n5 escapes at line 51 func() { n4 = n5 }() @@ -104,19 +104,28 @@ func nonEscapingReturnedPointer() vector3 { var escapedSlice []int func escapingReturnedSlice() { - s := make([]int, 3) // OUT: escapes at line 108 + s := make([]int, 3) // OUT: makeslice escapes at line 108 escapedSlice = returnIntSlice(s) } var escapedVector3 *vector3 func escapingReturnedPointer() { - b := vector3{4, 5, 6} // OUT: escapes at line 117 + b := vector3{4, 5, 6} // OUT: b escapes at line 117 c := scaleVector3(&b, 0.5) escapedVector3 = c } +var escapedArray [2](*vector3) + +func escapingReturnedPointer2() { + c := scaleVector3(&vector3{4, 5, 6}, 0.5) // OUT: Arg 0 of main.scaleVector3() call escapes at line 124 + escapedVector3 = c + a := [2](*vector3){&vector3{7, 8, 9}, &vector3{2, 3, 4}} // OUT: complit1 escapes at line 126 && complit2 escapes at line 126 + escapedArray = a +} + func recursiveScaleVector3(vec *vector3, n int) *vector3 { if n == 0 { return vec @@ -125,7 +134,7 @@ func recursiveScaleVector3(vec *vector3, n int) *vector3 { } func recursiveReturnedPointer() vector3 { - b := vector3{4, 5, 6} // OUT: escapes at unknown line + b := vector3{4, 5, 6} // OUT: b escapes at unknown line c := recursiveScaleVector3(&b, 1) return *c diff --git a/transform/testdata/allocs2.out.cover b/transform/testdata/allocs2.out.cover index e17f3d421e..a9e02123af 100644 --- a/transform/testdata/allocs2.out.cover +++ b/transform/testdata/allocs2.out.cover @@ -1,11 +1,14 @@ -testdata/allocs2.go:24.1,24.72 1 0 -testdata/allocs2.go:26.1,26.91 1 0 -testdata/allocs2.go:29.1,29.49 1 0 -testdata/allocs2.go:36.1,36.50 1 0 -testdata/allocs2.go:44.1,44.50 1 0 -testdata/allocs2.go:46.1,46.49 1 0 -testdata/allocs2.go:49.1,49.36 1 0 -testdata/allocs2.go:50.1,50.36 1 0 -testdata/allocs2.go:107.1,107.49 1 0 -testdata/allocs2.go:114.1,114.51 1 0 -testdata/allocs2.go:128.1,128.55 1 0 +testdata/allocs2.go:24.1,24.86 1 0 +testdata/allocs2.go:26.1,26.95 1 0 +testdata/allocs2.go:29.1,29.60 1 0 +testdata/allocs2.go:36.1,36.60 1 0 +testdata/allocs2.go:44.1,44.60 1 0 +testdata/allocs2.go:46.1,46.60 1 0 +testdata/allocs2.go:49.1,49.39 1 0 +testdata/allocs2.go:50.1,50.39 1 0 +testdata/allocs2.go:107.1,107.59 1 0 +testdata/allocs2.go:114.1,114.53 1 0 +testdata/allocs2.go:123.1,123.105 1 0 +testdata/allocs2.go:125.1,125.127 1 0 +testdata/allocs2.go:125.1,125.127 1 0 +testdata/allocs2.go:137.1,137.57 1 0