1
0
mirror of https://github.com/golang/go synced 2024-11-18 21:14:44 -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) v := b.receiver(fn, e.X, wantAddr, escaping, sel)
last := len(sel.Index()) - 1 last := len(sel.Index()) - 1
return &address{ 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, expr: e.Sel,
} }
@ -455,7 +455,15 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
return NewConst(tv.Value, tv.Type) 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() { if fn.debugInfo() {
emitDebugRef(fn, e, v, false) 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 last := len(indices) - 1
v := b.expr(fn, e.X) v := b.expr(fn, e.X)
v = emitImplicitSelections(fn, v, indices[:last]) v = emitImplicitSelections(fn, v, indices[:last])
v = emitFieldSelection(fn, v, indices[last], false, e.Sel.Pos()) v = emitFieldSelection(fn, v, indices[last], false, e.Sel)
emitDebugRef(fn, e.Sel, v, false)
return v 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 // 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 // will be the field's address; otherwise the result will be the
// field's value. // 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) fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
if isPointer(v.Type()) { if isPointer(v.Type()) {
instr := &FieldAddr{ instr := &FieldAddr{
X: v, X: v,
Field: index, Field: index,
} }
instr.setPos(pos) instr.setPos(id.Pos())
instr.setType(types.NewPointer(fld.Type())) instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr) v = f.emit(instr)
// Load the field's value iff we don't want its address. // 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, X: v,
Field: index, Field: index,
} }
instr.setPos(pos) instr.setPos(id.Pos())
instr.setType(fld.Type()) instr.setType(fld.Type())
v = f.emit(instr) v = f.emit(instr)
} }
emitDebugRef(f, id, v, wantAddr)
return v 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 // 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.) // 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 // If the identifier is a field selector and its base expression is
// value assigned to it in its definition. Similarly, the Value of an // non-addressable, then VarValue returns the value of that field.
// identifier that is the LHS of an assignment is the value assigned // For example:
// to it in that statement. In all these examples, VarValue(x) returns // func f() struct {x int}
// the value of x and isAddr==false. // f().x // VarValue(x) returns a *Field instruction of type int
// //
// var x X // All other identifiers denote addressable locations (variables).
// var x = X{} // For them, VarValue may return either the variable's address or its
// x := X{} // value, even when the expression is evaluated only for its value; the
// x = X{} // situation is reported by isAddr, the second component of the result.
// //
// When an identifier appears in an lvalue context other than as the // If !isAddr, the returned value is the one associated with the
// LHS of an assignment, the resulting Value is the var's address, not // specific identifier. For example,
// its value. This situation is reported by isAddr, the second // var x int // VarValue(x) returns Const 0 here
// component of the result. In these examples, VarValue(x) returns // x = 1 // VarValue(x) returns Const 1 here
// the address of x and isAddr==true.
// //
// x.y = 0 // It is not specified whether the value or the address is returned in
// x[0] = 0 // any particular case, as it may depend upon optimizations performed
// _ = x[:] (where x is an array) // during SSA code generation, such as registerization, constant
// _ = &x // folding, avoidance of materialization of subexpressions, etc.
// x.method() (iff method is on &x)
// //
func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) { 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. // All references to a var are local to some function, possibly init.

View File

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