Skip to content

Commit 3dc446d

Browse files
committed
transform: don't escape append arguments if the return value doesn't
1 parent 0edeaf6 commit 3dc446d

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed

transform/allocs.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,18 @@ func valueEscapesAt(value llvm.Value) llvm.Value {
154154
return use
155155
}
156156
case llvm.Call:
157-
if !hasFlag(use, value, "nocapture") {
158-
return use
157+
if hasFlag(use, value, "nocapture") {
158+
break
159+
}
160+
// If built-in append function escapes its first argument if and
161+
// only if the returned slice pointer escapes.
162+
if fn := use.CalledValue(); !fn.IsAFunction().IsNil() && fn.Name() == "runtime.sliceAppend" {
163+
if at := elemEscapesAt(use, 0); !at.IsNil() {
164+
return at
165+
}
166+
break
159167
}
168+
return use
160169
case llvm.ICmp:
161170
// Comparing pointers don't let the pointer escape.
162171
// This is often a compiler-inserted nil check.
@@ -170,6 +179,32 @@ func valueEscapesAt(value llvm.Value) llvm.Value {
170179
return llvm.Value{}
171180
}
172181

182+
// elemEscapesAt is like valueEscapesAt, but for an element of a tuple value.
183+
func elemEscapesAt(value llvm.Value, elemIndex uint32) llvm.Value {
184+
uses := getUses(value)
185+
for _, use := range uses {
186+
if use.IsAInstruction().IsNil() {
187+
panic("expected instruction use")
188+
}
189+
switch use.InstructionOpcode() {
190+
case llvm.ExtractValue:
191+
for _, ind := range use.Indices() {
192+
if ind == elemIndex {
193+
if at := valueEscapesAt(use); !at.IsNil() {
194+
return at
195+
}
196+
break
197+
}
198+
}
199+
default:
200+
if at := valueEscapesAt(use); !at.IsNil() {
201+
return at
202+
}
203+
}
204+
}
205+
return llvm.Value{}
206+
}
207+
173208
// logAlloc prints a message to stderr explaining why the given object had to be
174209
// allocated on the heap.
175210
func logAlloc(logger func(token.Position, string), allocCall llvm.Value, reason string) {

transform/testdata/allocs2.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func main() {
2323
s4 := make([]byte, 300) // OUT: object allocated on the heap: object size 300 exceeds maximum stack allocation size 256
2424
readByteSlice(s4)
2525

26-
s5 := make([]int, 4) // OUT: object allocated on the heap: escapes at line 27
26+
s5 := make([]int, 4)
2727
_ = append(s5, 5)
2828

2929
s6 := make([]int, 3)
@@ -58,6 +58,11 @@ func main() {
5858
var rbuf [5]rune
5959
s = string(rbuf[:])
6060
println(s)
61+
62+
sl := make([]rune, 4)
63+
sl_1 := append(sl, 5)
64+
sl_2 := append(sl_1, 5)
65+
println(string(sl_2))
6166
}
6267

6368
func derefInt(x *int) int {

0 commit comments

Comments
 (0)