Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions transform/allocs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
20 changes: 18 additions & 2 deletions transform/allocs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
31 changes: 20 additions & 11 deletions transform/testdata/allocs2.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,33 @@ 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
func() int {
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
}()
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
25 changes: 14 additions & 11 deletions transform/testdata/allocs2.out.cover
Original file line number Diff line number Diff line change
@@ -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