go/ssa: add position info to all Load/Store instructions

Was originally https://codereview.appspot.com/92880043/ by Richard Musiol.

Change-Id: If0b22cf962b94ef44edbac28a5e5af4acb950991
Reviewed-on: https://go-review.googlesource.com/2174
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 1460d48..4bab92d 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -347,7 +347,7 @@
 		if v == nil {
 			v = fn.lookup(obj, escaping)
 		}
-		return &address{addr: v, expr: e}
+		return &address{addr: v, pos: e.Pos(), expr: e}
 
 	case *ast.CompositeLit:
 		t := deref(fn.Pkg.typeOf(e))
@@ -359,7 +359,7 @@
 		}
 		v.Comment = "complit"
 		b.compLit(fn, v, e, true) // initialize in place
-		return &address{addr: v, expr: e}
+		return &address{addr: v, pos: e.Lbrace, expr: e}
 
 	case *ast.ParenExpr:
 		return b.addr(fn, e.X, escaping)
@@ -378,6 +378,7 @@
 		last := len(sel.Index()) - 1
 		return &address{
 			addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel),
+			pos:  e.Sel.Pos(),
 			expr: e.Sel,
 		}
 
@@ -410,10 +411,10 @@
 		}
 		v.setPos(e.Lbrack)
 		v.setType(et)
-		return &address{addr: fn.emit(v), expr: e}
+		return &address{addr: fn.emit(v), pos: e.Lbrack, expr: e}
 
 	case *ast.StarExpr:
-		return &address{addr: b.expr(fn, e.X), starPos: e.Star, expr: e}
+		return &address{addr: b.expr(fn, e.X), pos: e.Star, expr: e}
 	}
 
 	panic(fmt.Sprintf("unexpected address expression: %T", e))
@@ -891,7 +892,7 @@
 				}
 				iaddr.setType(types.NewPointer(vt))
 				fn.emit(iaddr)
-				emitStore(fn, iaddr, arg)
+				emitStore(fn, iaddr, arg, arg.Pos())
 			}
 			s := &Slice{X: a}
 			s.setType(st)
@@ -1044,17 +1045,19 @@
 	switch t := typ.Underlying().(type) {
 	case *types.Struct:
 		if !isZero && len(e.Elts) != t.NumFields() {
-			emitMemClear(fn, addr)
+			emitMemClear(fn, addr, e.Lbrace)
 			isZero = true
 		}
 		for i, e := range e.Elts {
 			fieldIndex := i
+			pos := e.Pos()
 			if kv, ok := e.(*ast.KeyValueExpr); ok {
 				fname := kv.Key.(*ast.Ident).Name
 				for i, n := 0, t.NumFields(); i < n; i++ {
 					sf := t.Field(i)
 					if sf.Name() == fname {
 						fieldIndex = i
+						pos = kv.Colon
 						e = kv.Value
 						break
 					}
@@ -1067,7 +1070,7 @@
 			}
 			faddr.setType(types.NewPointer(sf.Type()))
 			fn.emit(faddr)
-			b.exprInPlace(fn, &address{addr: faddr, expr: e}, e, isZero)
+			b.exprInPlace(fn, &address{addr: faddr, pos: pos, expr: e}, e, isZero)
 		}
 
 	case *types.Array, *types.Slice:
@@ -1086,7 +1089,7 @@
 		}
 
 		if !isZero && int64(len(e.Elts)) != at.Len() {
-			emitMemClear(fn, array)
+			emitMemClear(fn, array, e.Lbrace)
 			isZero = true
 		}
 
@@ -1108,20 +1111,20 @@
 			}
 			iaddr.setType(types.NewPointer(at.Elem()))
 			fn.emit(iaddr)
-			b.exprInPlace(fn, &address{addr: iaddr, expr: e}, e, isZero)
+			b.exprInPlace(fn, &address{addr: iaddr, pos: e.Pos(), expr: e}, e, isZero)
 		}
 		if t != at { // slice
 			s := &Slice{X: array}
 			s.setPos(e.Lbrace)
 			s.setType(typ)
-			emitStore(fn, addr, fn.emit(s))
+			emitStore(fn, addr, fn.emit(s), e.Lbrace)
 		}
 
 	case *types.Map:
 		m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))}
 		m.setPos(e.Lbrace)
 		m.setType(typ)
-		emitStore(fn, addr, fn.emit(m))
+		emitStore(fn, addr, fn.emit(m), e.Lbrace)
 		for _, e := range e.Elts {
 			e := e.(*ast.KeyValueExpr)
 			loc := &element{
@@ -1337,7 +1340,7 @@
 		// In a single-type case, y has that type.
 		// In multi-type cases, 'case nil' and default,
 		// y has the same type as the interface operand.
-		emitStore(fn, fn.addNamedLocal(obj), x)
+		emitStore(fn, fn.addNamedLocal(obj), x, obj.Pos())
 	}
 	fn.targets = &targets{
 		tail:   fn.targets,
@@ -1588,8 +1591,9 @@
 // rangeIndexed emits to fn the header for an integer-indexed loop
 // over array, *array or slice value x.
 // The v result is defined only if tv is non-nil.
+// forPos is the position of the "for" token.
 //
-func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value, loop, done *BasicBlock) {
+func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) {
 	//
 	//      length = len(x)
 	//      index = -1
@@ -1623,7 +1627,7 @@
 	}
 
 	index := fn.addLocal(tInt, token.NoPos)
-	emitStore(fn, index, intConst(-1))
+	emitStore(fn, index, intConst(-1), pos)
 
 	loop = fn.newBasicBlock("rangeindex.loop")
 	emitJump(fn, loop)
@@ -1635,7 +1639,7 @@
 		Y:  vOne,
 	}
 	incr.setType(tInt)
-	emitStore(fn, index, fn.emit(incr))
+	emitStore(fn, index, fn.emit(incr), pos)
 
 	body := fn.newBasicBlock("rangeindex.body")
 	done = fn.newBasicBlock("rangeindex.done")
@@ -1814,10 +1818,10 @@
 	var loop, done *BasicBlock
 	switch rt := x.Type().Underlying().(type) {
 	case *types.Slice, *types.Array, *types.Pointer: // *array
-		k, v, loop, done = b.rangeIndexed(fn, x, tv)
+		k, v, loop, done = b.rangeIndexed(fn, x, tv, s.For)
 
 	case *types.Chan:
-		k, loop, done = b.rangeChan(fn, x, tk, s.TokPos)
+		k, loop, done = b.rangeChan(fn, x, tk, s.For)
 
 	case *types.Map, *types.Basic: // string
 		k, v, loop, done = b.rangeIter(fn, x, tk, tv, s.For)
@@ -1955,7 +1959,7 @@
 			// Function has named result parameters (NRPs).
 			// Perform parallel assignment of return operands to NRPs.
 			for i, r := range results {
-				emitStore(fn, fn.namedResults[i], r)
+				emitStore(fn, fn.namedResults[i], r, s.Return)
 			}
 		}
 		// Run function calls deferred in this
@@ -2206,7 +2210,7 @@
 		done = init.newBasicBlock("init.done")
 		emitIf(init, emitLoad(init, initguard), done, doinit)
 		init.currentBlock = doinit
-		emitStore(init, initguard, vTrue)
+		emitStore(init, initguard, vTrue, token.NoPos)
 
 		// Call the init() function of each package we import.
 		for _, pkg := range p.info.Pkg.Imports() {
@@ -2234,7 +2238,7 @@
 			// 1:1 initialization: var x, y = a(), b()
 			var lval lvalue
 			if v := varinit.Lhs[0]; v.Name() != "_" {
-				lval = &address{addr: p.values[v].(*Global)}
+				lval = &address{addr: p.values[v].(*Global), pos: v.Pos()}
 			} else {
 				lval = blank{}
 			}
@@ -2246,7 +2250,7 @@
 				if v.Name() == "_" {
 					continue
 				}
-				emitStore(init, p.values[v].(*Global), emitExtract(init, tuple, i))
+				emitStore(init, p.values[v].(*Global), emitExtract(init, tuple, i), v.Pos())
 			}
 		}
 	}
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index 84246c6..261d6e0 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -246,10 +246,11 @@
 // emitStore emits to f an instruction to store value val at location
 // addr, applying implicit conversions as required by assignability rules.
 //
-func emitStore(f *Function, addr, val Value) *Store {
+func emitStore(f *Function, addr, val Value, pos token.Pos) *Store {
 	s := &Store{
 		Addr: addr,
 		Val:  emitConv(f, val, deref(addr.Type())),
+		pos:  pos,
 	}
 	f.emit(s)
 	return s
@@ -430,9 +431,9 @@
 }
 
 // emitMemClear emits to f code to zero the value pointed to by ptr.
-func emitMemClear(f *Function, ptr Value) {
+func emitMemClear(f *Function, ptr Value, pos token.Pos) {
 	// TODO(adonovan): define and use a 'memclr' intrinsic for aggregate types.
-	emitStore(f, ptr, zeroValue(f, deref(ptr.Type())))
+	emitStore(f, ptr, zeroValue(f, deref(ptr.Type())), pos)
 }
 
 // createRecoverBlock emits to f a block of code to return after a
diff --git a/go/ssa/lvalue.go b/go/ssa/lvalue.go
index e58bc24..8342645 100644
--- a/go/ssa/lvalue.go
+++ b/go/ssa/lvalue.go
@@ -27,20 +27,19 @@
 
 // An address is an lvalue represented by a true pointer.
 type address struct {
-	addr    Value
-	starPos token.Pos // source position, if from explicit *addr
-	expr    ast.Expr  // source syntax [debug mode]
+	addr Value
+	pos  token.Pos // source position
+	expr ast.Expr  // source syntax [debug mode]
 }
 
 func (a *address) load(fn *Function) Value {
 	load := emitLoad(fn, a.addr)
-	load.pos = a.starPos
+	load.pos = a.pos
 	return load
 }
 
 func (a *address) store(fn *Function, v Value) {
-	store := emitStore(fn, a.addr, v)
-	store.pos = a.starPos
+	store := emitStore(fn, a.addr, v, a.pos)
 	if a.expr != nil {
 		// store.Val is v, converted for assignability.
 		emitDebugRef(fn, a.expr, store.Val, false)
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index bc5e9da..406611e 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -564,8 +564,12 @@
 // and a boolean indicating the success of the receive.  The
 // components of the tuple are accessed using Extract.
 //
-// Pos() returns the ast.UnaryExpr.OpPos or ast.RangeStmt.TokPos (for
-// ranging over a channel), if explicit in the source.
+// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source.
+// For receive operations (ARROW) implicit in ranging over a channel,
+// Pos() returns the ast.RangeStmt.For.
+// For implicit memory loads (STAR), Pos() returns the position of the
+// most closely associated source-level construct; the details are not
+// specified.
 //
 // Example printed form:
 // 	t0 = *x
@@ -1162,7 +1166,10 @@
 // The Store instruction stores Val at address Addr.
 // Stores can be of arbitrary types.
 //
-// Pos() returns the ast.StarExpr.Star, if explicit in the source.
+// Pos() returns the position of the source-level construct most closely
+// associated with the memory store operation.
+// Since implicit memory stores are numerous and varied and depend upon
+// implementation choices, the details are not specified.
 //
 // Example printed form:
 // 	*x = y
diff --git a/go/ssa/testmain.go b/go/ssa/testmain.go
index 422c7db..ae9ec1c 100644
--- a/go/ssa/testmain.go
+++ b/go/ssa/testmain.go
@@ -247,7 +247,7 @@
 		pname := fn.emit(fa)
 
 		// Emit: *pname = "testfunc"
-		emitStore(fn, pname, stringConst(testfunc.Name()))
+		emitStore(fn, pname, stringConst(testfunc.Name()), token.NoPos)
 
 		// Emit: pfunc = &pitem.F
 		fa = &FieldAddr{X: pitem, Field: 1} // .F
@@ -255,7 +255,7 @@
 		pfunc := fn.emit(fa)
 
 		// Emit: *pfunc = testfunc
-		emitStore(fn, pfunc, testfunc)
+		emitStore(fn, pfunc, testfunc, token.NoPos)
 	}
 
 	// Emit: slice array[:]