mirror of
https://github.com/golang/go
synced 2024-11-18 23:34:45 -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:
parent
09286920ba
commit
514bdfb1b7
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
8
go/ssa/testdata/objlookup.go
vendored
8
go/ssa/testdata/objlookup.go
vendored
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user