Skip to content

Commit

Permalink
cmd/gc: allocate backing storage for non-escaping interfaces on stack
Browse files Browse the repository at this point in the history
Extend escape analysis to convT2E and conT2I. If the interface value
does not escape supply runtime with a stack buffer for the object copy.

This is a straight port from .c to .go of Dmitry's patch

Change-Id: Ic315dd50d144d94dd3324227099c116be5ca70b6
Reviewed-on: https://go-review.googlesource.com/8201
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
  • Loading branch information
dr2chase committed Mar 30, 2015
1 parent cf7461c commit 2270133
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/cmd/internal/gc/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ const runtimeimport = "" +
"func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n" +
"func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n" +
"func @\"\".convI2I (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n" +
"func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 *any) (@\"\".ret·1 any)\n" +
"func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 *any) (@\"\".ret·1 any)\n" +
"func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 *any, @\"\".buf·4 *any) (@\"\".ret·1 any)\n" +
"func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 *any, @\"\".buf·6 *any) (@\"\".ret·1 any)\n" +
"func @\"\".assertE2E (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" +
"func @\"\".assertE2E2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" +
"func @\"\".assertE2I (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" +
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/internal/gc/builtin/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func slicestringcopy(to any, fr any) int
func typ2Itab(typ *byte, typ2 *byte, cache **byte) (ret *byte)
func convI2E(elem any) (ret any)
func convI2I(typ *byte, elem any) (ret any)
func convT2E(typ *byte, elem *any) (ret any)
func convT2I(typ *byte, typ2 *byte, cache **byte, elem *any) (ret any)
func convT2E(typ *byte, elem, buf *any) (ret any)
func convT2I(typ *byte, typ2 *byte, cache **byte, elem, buf *any) (ret any)

// interface type assertions x.(T)
func assertE2E(typ *byte, iface any, ret *any)
Expand Down
5 changes: 2 additions & 3 deletions src/cmd/internal/gc/esc.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,12 +653,11 @@ func esc(e *EscState, n *Node, up *Node) {
}
}

case OCONV, OCONVNOP:
case OCONV,
OCONVNOP:
escassign(e, n, n.Left)

case OCONVIFACE:
// We don't allocate storage for OCONVIFACE on stack yet,
// but mark it as EscNone merely to get debug output for tests.
n.Esc = EscNone // until proven otherwise
e.noesc = list(e.noesc, n)
n.Escloopdepth = e.loopdepth
Expand Down
18 changes: 17 additions & 1 deletion src/cmd/internal/gc/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -1040,9 +1040,25 @@ func walkexpr(np **Node, init **NodeList) {
} else {
ll = list(ll, Nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil))
}
dowidth(n.Left.Type)
r := nodnil()
if n.Esc == EscNone && n.Left.Type.Width <= 1024 {
// Allocate stack buffer for value stored in interface.
r = temp(n.Left.Type)
r = Nod(OAS, r, nil) // zero temp
typecheck(&r, Etop)
*init = list(*init, r)
r = Nod(OADDR, r.Left, nil)
typecheck(&r, Erv)
}
ll = list(ll, r)
}

substArgTypes(fn, n.Left.Type, n.Type)
if !Isinter(n.Left.Type) {
substArgTypes(fn, n.Left.Type, n.Left.Type, n.Type)
} else {
substArgTypes(fn, n.Left.Type, n.Type)
}
dowidth(fn.Type)
n = Nod(OCALL, fn, nil)
n.List = ll
Expand Down
12 changes: 8 additions & 4 deletions src/runtime/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,15 @@ func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab {
return tab
}

func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) {
func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e interface{}) {
ep := (*eface)(unsafe.Pointer(&e))
if isDirectIface(t) {
ep._type = t
typedmemmove(t, unsafe.Pointer(&ep.data), elem)
} else {
x := newobject(t)
if x == nil {
x = newobject(t)
}
// TODO: We allocate a zeroed object only to overwrite it with
// actual data. Figure out how to avoid zeroing. Also below in convT2I.
typedmemmove(t, x, elem)
Expand All @@ -146,7 +148,7 @@ func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) {
return
}

func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer) (i fInterface) {
func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer, x unsafe.Pointer) (i fInterface) {
tab := (*itab)(atomicloadp(unsafe.Pointer(cache)))
if tab == nil {
tab = getitab(inter, t, false)
Expand All @@ -157,7 +159,9 @@ func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer)
pi.tab = tab
typedmemmove(t, unsafe.Pointer(&pi.data), elem)
} else {
x := newobject(t)
if x == nil {
x = newobject(t)
}
typedmemmove(t, x, elem)
pi.tab = tab
pi.data = x
Expand Down
40 changes: 40 additions & 0 deletions src/runtime/iface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,43 @@ func BenchmarkAssertE2E2Blank(b *testing.B) {
_, ok = e.(interface{})
}
}

func TestNonEscapingConvT2E(t *testing.T) {
m := make(map[interface{}]bool)
m[42] = true
if !m[42] {
t.Fatalf("42 is not present in the map")
}
if m[0] {
t.Fatalf("0 is present in the map")
}

n := testing.AllocsPerRun(1000, func() {
if m[0] {
t.Fatalf("0 is present in the map")
}
})
if n != 0 {
t.Fatalf("want 0 allocs, got %v", n)
}
}

func TestNonEscapingConvT2I(t *testing.T) {
m := make(map[I1]bool)
m[TM(42)] = true
if !m[TM(42)] {
t.Fatalf("42 is not present in the map")
}
if m[TM(0)] {
t.Fatalf("0 is present in the map")
}

n := testing.AllocsPerRun(1000, func() {
if m[TM(0)] {
t.Fatalf("0 is present in the map")
}
})
if n != 0 {
t.Fatalf("want 0 allocs, got %v", n)
}
}
2 changes: 1 addition & 1 deletion test/escape2.go
Original file line number Diff line number Diff line change
Expand Up @@ -1787,7 +1787,7 @@ func makemap1() map[int]int {

func makemap2() {
m := make(map[int]int) // ERROR "make\(map\[int\]int\) escapes to heap"
sink = m // ERROR "m escapes to heap"
sink = m // ERROR "m escapes to heap"
}

func nonescapingEface(m map[interface{}]bool) bool { // ERROR "m does not escape"
Expand Down

0 comments on commit 2270133

Please sign in to comment.