1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:54:42 -07:00

go.tools/go/ssa: improve generated code for addressable expressions.

If an expression is addressable, we compute its address then load, rather than
extracting the value of subelements.  For aggregates this avoids large copies.
Example:
        var x [2]struct{y [3]int}
        print(x[1].y[2])

Was:
t0 = local [3]struct{x [5]int} (x)                 *[3]struct{x [5]int}
t1 = *t0                                            [3]struct{x [5]int}
t2 = t1[1:int]                                         struct{x [5]int}
t3 = t2.x [#0]                                                   [5]int
t4 = t3[2:int]                                                      int

Now:
t1 = &t0[1:int]                                       *struct{x [5]int}
t2 = &t1.x [#0]                                                 *[5]int
t3 = &t2[2:int]                                                    *int
t4 = *t3                                                            int

Also:
- make emitFieldSelections responsible for calling emitDebugRef, as
  one of its two calls was forgetting to do it.
- relax the specification of (*Program).VarValue because not all
  subexpressions are materalized as values now.
- fix up the objlookup.go test expectations to match.

go/ssa/interp test runs 10% faster.

Thanks to Peter Collingbourne for pointing this out.

LGTM=pcc
R=pcc, gri
CC=golang-codereviews
https://golang.org/cl/109710043
This commit is contained in:
Alan Donovan 2014-07-17 15:06:09 -04:00
parent 09286920ba
commit 514bdfb1b7
4 changed files with 37 additions and 30 deletions

View File

@ -363,7 +363,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
last := len(sel.Index()) - 1
return &address{
addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel.Pos()),
addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel),
expr: e.Sel,
}
@ -455,7 +455,15 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
return NewConst(tv.Value, tv.Type)
}
v := b.expr0(fn, e, tv)
var v Value
if tv.Addressable() {
// Prefer pointer arithmetic ({Index,Field}Addr) followed
// by Load over subelement extraction (e.g. Index, Field),
// to avoid large copies.
v = b.addr(fn, e, false).load(fn)
} else {
v = b.expr0(fn, e, tv)
}
if fn.debugInfo() {
emitDebugRef(fn, e, v, false)
}
@ -656,8 +664,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
last := len(indices) - 1
v := b.expr(fn, e.X)
v = emitImplicitSelections(fn, v, indices[:last])
v = emitFieldSelection(fn, v, indices[last], false, e.Sel.Pos())
emitDebugRef(fn, e.Sel, v, false)
v = emitFieldSelection(fn, v, indices[last], false, e.Sel)
return v
}

View File

@ -388,15 +388,16 @@ func emitImplicitSelections(f *Function, v Value, indices []int) Value {
// If wantAddr, the input must be a pointer-to-struct and the result
// will be the field's address; otherwise the result will be the
// field's value.
// Ident id is used for position and debug info.
//
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, pos token.Pos) Value {
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
if isPointer(v.Type()) {
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setPos(pos)
instr.setPos(id.Pos())
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff we don't want its address.
@ -408,10 +409,11 @@ func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, pos toke
X: v,
Field: index,
}
instr.setPos(pos)
instr.setPos(id.Pos())
instr.setType(fld.Type())
v = f.emit(instr)
}
emitDebugRef(f, id, v, wantAddr)
return v
}

View File

@ -235,28 +235,26 @@ func (prog *Program) ConstValue(obj *types.Const) *Const {
// pkg is the package enclosing the reference. (A reference to a var
// always occurs within a function, so we need to know where to find it.)
//
// The Value of a defining (as opposed to referring) identifier is the
// value assigned to it in its definition. Similarly, the Value of an
// identifier that is the LHS of an assignment is the value assigned
// to it in that statement. In all these examples, VarValue(x) returns
// the value of x and isAddr==false.
// If the identifier is a field selector and its base expression is
// non-addressable, then VarValue returns the value of that field.
// For example:
// func f() struct {x int}
// f().x // VarValue(x) returns a *Field instruction of type int
//
// var x X
// var x = X{}
// x := X{}
// x = X{}
// All other identifiers denote addressable locations (variables).
// For them, VarValue may return either the variable's address or its
// value, even when the expression is evaluated only for its value; the
// situation is reported by isAddr, the second component of the result.
//
// When an identifier appears in an lvalue context other than as the
// LHS of an assignment, the resulting Value is the var's address, not
// its value. This situation is reported by isAddr, the second
// component of the result. In these examples, VarValue(x) returns
// the address of x and isAddr==true.
// If !isAddr, the returned value is the one associated with the
// specific identifier. For example,
// var x int // VarValue(x) returns Const 0 here
// x = 1 // VarValue(x) returns Const 1 here
//
// x.y = 0
// x[0] = 0
// _ = x[:] (where x is an array)
// _ = &x
// x.method() (iff method is on &x)
// It is not specified whether the value or the address is returned in
// any particular case, as it may depend upon optimizations performed
// during SSA code generation, such as registerization, constant
// folding, avoidance of materialization of subexpressions, etc.
//
func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
// All references to a var are local to some function, possibly init.

View File

@ -78,13 +78,13 @@ func main() {
print(v6) // v6::Const
var v7 S // &v7::Alloc
v7.x = 1 // &v7::Alloc x::Const
print(v7.x) // v7::UnOp x::Field
v7.x = 1 // &v7::Alloc &x::FieldAddr
print(v7.x) // &v7::Alloc &x::FieldAddr
var v8 [1]int // &v8::Alloc
v8[0] = 0 // &v8::Alloc
print(v8[:]) // &v8::Alloc
_ = v8[0] // v8::UnOp (load from Alloc)
_ = v8[0] // &v8::Alloc
_ = v8[:][0] // &v8::Alloc
v8ptr := &v8 // v8ptr::Alloc &v8::Alloc
_ = v8ptr[0] // v8ptr::Alloc
@ -145,7 +145,7 @@ func main() {
// .Op is an inter-package FieldVal-selection.
var err os.PathError // &err::Alloc
_ = err.Op // err::UnOp Op::Field
_ = err.Op // &err::Alloc &Op::FieldAddr
_ = &err.Op // &err::Alloc &Op::FieldAddr
// Exercise corner-cases of lvalues vs rvalues.