Skip to content
This repository has been archived by the owner on Dec 14, 2023. It is now read-only.

Commit

Permalink
Allocation stack lowering based on escape analysis
Browse files Browse the repository at this point in the history
Now that we can run libgo benchmarks:
https://codereview.appspot.com/103550047/
we can measure the effect of optimizations to see if they are worth adding
to the compiler.

This introduces an ssaopt package to hold SSA-level optimizations, to which
we add an allocation stack lowering pass based on a simple intraprocedural
escape analysis. In an experiment comparing libgo benchmark performance
before and after this change, this pass yielded a geo-mean 5.4% (3.6%-7.2%
at a 95% confidence interval) improvement in performance.
  • Loading branch information
pcc committed Jun 27, 2014
1 parent 8ee8522 commit 0e4bfab
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ bootstrap: workdir/.bootstrap-stamp
install: bootstrap
./install.sh $(prefix)

workdir/.bootstrap-stamp: workdir/.update-libgo-stamp workdir/.update-clang-stamp bootstrap.sh *.go build/*.go cmd/gllgo/*.go cmd/cc-wrapper/*.go debug/*.go
workdir/.bootstrap-stamp: workdir/.update-libgo-stamp workdir/.update-clang-stamp bootstrap.sh *.go build/*.go cmd/gllgo/*.go cmd/cc-wrapper/*.go debug/*.go ssaopt/*.go
./bootstrap.sh $(bootstrap) -j$(j)

workdir/.update-clang-stamp: update_clang.sh
Expand Down
3 changes: 3 additions & 0 deletions ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"code.google.com/p/go.tools/go/ssa"
"code.google.com/p/go.tools/go/ssa/ssautil"
"code.google.com/p/go.tools/go/types"
"github.com/go-llvm/llgo/ssaopt"
"github.com/go-llvm/llvm"
)

Expand Down Expand Up @@ -226,6 +227,8 @@ func (u *unit) defineFunction(f *ssa.Function) {
return
}

ssaopt.LowerAllocsToStack(f)

if u.DumpSSA {
f.WriteTo(os.Stderr)
}
Expand Down
94 changes: 94 additions & 0 deletions ssaopt/esc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2014 The llgo Authors.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package ssaopt

import (
"go/token"

"code.google.com/p/go.tools/go/ssa"
)

func escapes(val ssa.Value, bb *ssa.BasicBlock, pending []ssa.Value) bool {
for _, p := range pending {
if val == p {
return false
}
}

for _, ref := range *val.Referrers() {
switch ref := ref.(type) {
case *ssa.Phi:
// We must consider the variable to have escaped if it is
// possible for the program to see more than one "version"
// of the variable at once, as this requires the program
// to use heap allocation for the multiple versions.
//
// I (pcc) think that this is only possible (without stores)
// in the case where a phi node that (directly or indirectly)
// refers to the allocation dominates the allocation.
if ref.Block().Dominates(bb) {
return true
}
if escapes(ref, bb, append(pending, val)) {
return true
}

case *ssa.BinOp, *ssa.ChangeType, *ssa.Convert, *ssa.ChangeInterface, *ssa.MakeInterface, *ssa.Slice, *ssa.FieldAddr, *ssa.IndexAddr, *ssa.TypeAssert, *ssa.Extract:
if escapes(ref.(ssa.Value), bb, append(pending, val)) {
return true
}

case *ssa.Range, *ssa.DebugRef:
continue

case *ssa.UnOp:
if ref.Op == token.MUL || ref.Op == token.ARROW {
continue
}
if escapes(ref, bb, append(pending, val)) {
return true
}

case *ssa.Store:
if val == ref.Val {
return true
}

case *ssa.Call:
if builtin, ok := ref.Call.Value.(*ssa.Builtin); ok {
switch builtin.Name() {
case "cap", "len", "copy", "ssa:wrapnilchk":
continue
case "append":
if ref.Call.Args[0] == val && escapes(ref, bb, append(pending, val)) {
return true
}
default:
return true
}
} else {
return true
}

default:
return true
}
}

return false
}

func LowerAllocsToStack(f *ssa.Function) {
pending := make([]ssa.Value, 0, 10)

for _, b := range f.Blocks {
for _, instr := range b.Instrs {
if alloc, ok := instr.(*ssa.Alloc); ok && alloc.Heap && !escapes(alloc, alloc.Block(), pending) {
alloc.Heap = false
f.Locals = append(f.Locals, alloc)
}
}
}
}

0 comments on commit 0e4bfab

Please sign in to comment.