even pointers to stack data must not be kept in local variables.
</p>
+<p>
+Assembly functions should always be given Go prototypes,
+both to provide pointer information for the arguments and results
+and to let <code>go</code> <code>vet</code> check that
+the offsets being used to access them are correct.
+</p>
+
<h2 id="architectures">Architecture-specific details</h2>
<p>
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Power opcode.
e.pdepth--
}
+// This special tag is applied to uintptr variables
+// that we believe may hold unsafe.Pointers for
+// calls into assembly functions.
+// It is logically a constant, but using a var
+// lets us take the address below to get a *string.
+var unsafeUintptrTag = "unsafe-uintptr"
+
func esctag(e *EscState, func_ *Node) {
func_.Esc = EscFuncTagged
}
}
+ // Assume that uintptr arguments must be held live across the call.
+ // This is most important for syscall.Syscall.
+ // See golang.org/issue/13372.
+ // This really doesn't have much to do with escape analysis per se,
+ // but we are reusing the ability to annotate an individual function
+ // argument and pass those annotations along to importing code.
+ narg := 0
+ for t := getinargx(func_.Type).Type; t != nil; t = t.Down {
+ narg++
+ if t.Type.Etype == TUINTPTR {
+ if Debug['m'] != 0 {
+ var name string
+ if t.Sym != nil {
+ name = t.Sym.Name
+ } else {
+ name = fmt.Sprintf("arg#%d", narg)
+ }
+ Warnl(int(func_.Lineno), "%v assuming %v is unsafe uintptr", funcSym(func_), name)
+ }
+ t.Note = &unsafeUintptrTag
+ }
+ }
+
return
}
n.Esc = EscNever
n.Name.Curfn = Curfn
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
+ if Debug['h'] != 0 {
+ println("H", n, n.Orig, funcSym(Curfn).Name)
+ }
dowidth(t)
n.Xoffset = 0
case OVARKILL:
gvarkill(n.Left)
+
+ case OVARLIVE:
+ gvarlive(n.Left)
}
ret:
continue
}
- if (p.As == obj.AVARDEF || p.As == obj.AVARKILL) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
+ if (p.As == obj.AVARDEF || p.As == obj.AVARKILL || p.As == obj.AVARLIVE) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
// VARDEFs are interspersed with other code, and a jump might be using the
// VARDEF as a target. Replace with a no-op instead. A later pass will remove
var kill *Node
for l := order.temp; l != mark; l = l.Next {
+ if l.N.Name.Keepalive {
+ l.N.Name.Keepalive = false
+ kill = Nod(OVARLIVE, l.N, nil)
+ typecheck(&kill, Etop)
+ *out = list(*out, kill)
+ }
kill = Nod(OVARKILL, l.N, nil)
typecheck(&kill, Etop)
*out = list(*out, kill)
orderexpr(&n.Left, order, nil)
orderexpr(&n.Right, order, nil) // ODDDARG temp
ordercallargs(&n.List, order)
+
+ if n.Op == OCALLFUNC {
+ for l, t := n.List, getinargx(n.Left.Type).Type; l != nil && t != nil; l, t = l.Next, t.Down {
+ // Check for "unsafe-uintptr" tag provided by escape analysis.
+ // If present and the argument is really a pointer being converted
+ // to uintptr, arrange for the pointer to be kept alive until the call
+ // returns, by copying it into a temp and marking that temp
+ // still alive when we pop the temp stack.
+ if t.Note != nil && *t.Note == unsafeUintptrTag {
+ xp := &l.N
+ for (*xp).Op == OCONVNOP && !Isptr[(*xp).Type.Etype] {
+ xp = &(*xp).Left
+ }
+ x := *xp
+ if Isptr[x.Type.Etype] {
+ x = ordercopyexpr(x, x.Type, order, 0)
+ x.Name.Keepalive = true
+ *xp = x
+ }
+ }
+ }
+ }
}
// Ordermapassign appends n to order->out, introducing temporaries
default:
Fatalf("orderstmt %v", Oconv(int(n.Op), 0))
- case OVARKILL:
+ case OVARKILL, OVARLIVE:
order.out = list(order.out, n)
case OAS:
switch n.Class {
case PAUTO, PPARAM, PPARAMOUT:
- Thearch.Gins(as, nil, n)
+ if as == obj.AVARLIVE {
+ Thearch.Gins(as, n, nil)
+ } else {
+ Thearch.Gins(as, nil, n)
+ }
}
}
gvardefx(n, obj.AVARKILL)
}
+func gvarlive(n *Node) {
+ gvardefx(n, obj.AVARLIVE)
+}
+
func removevardef(firstp *obj.Prog) {
for p := firstp; p != nil; p = p.Link {
- for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) {
+ for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
p.Link = p.Link.Link
}
if p.To.Type == obj.TYPE_BRANCH {
- for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL) {
+ for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL || p.To.Val.(*obj.Prog).As == obj.AVARLIVE) {
p.To.Val = p.To.Val.(*obj.Prog).Link
}
}
return
}
- fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p)
+ fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %p %v\n", funcSym(Curfn), n, n, n.Class, p, p)
for l := fn.Func.Dcl; l != nil; l = l.Next {
fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class)
}
goto ret
// can't matter
- case OCFUNC, OVARKILL:
+ case OCFUNC, OVARKILL, OVARLIVE:
goto ret
case OBLOCK:
for f := firstf; f != nil; f = f.Link {
p := f.Prog
+ // AVARLIVE must be considered a use, do not skip it.
+ // Otherwise the variable will be optimized away,
+ // and the whole point of AVARLIVE is to keep it on the stack.
if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
continue
}
Captured bool // is the variable captured by a closure
Byval bool // is the variable captured by value or by reference
Needzero bool // if it contains pointers, needs to be zeroed on function entry
+ Keepalive bool // mark value live across unknown assembly call
}
type Param struct {
OCFUNC // reference to c function pointer (not go func value)
OCHECKNIL // emit code to ensure pointer/interface not nil
OVARKILL // variable is dead
+ OVARLIVE // variable is alive
// thearch-specific registers
OREGISTER // a register, such as AX.
OEMPTY,
OGOTO,
OXFALL,
- OVARKILL:
+ OVARKILL,
+ OVARLIVE:
ok |= Etop
break OpSwitch
ODCLCONST,
ODCLTYPE,
OCHECKNIL,
- OVARKILL:
+ OVARKILL,
+ OVARLIVE:
break
case OBLOCK:
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the MIPS opcode.
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Power opcode.
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
AUSEFIELD
AVARDEF
AVARKILL
+ AVARLIVE
A_ARCHSPECIFIC
)
}
func Aconv(a int) string {
- if a < A_ARCHSPECIFIC {
+ if 0 <= a && a < len(Anames) {
return Anames[a]
}
for i := range aSpace {
"UNDEF",
"USEFIELD",
"VARDEF",
+ "VARLIVE",
"VARKILL",
}
// use is a no-op, but the compiler cannot see that it is.
// Calling use(p) ensures that p is kept live until that point.
+// This was needed until Go 1.6 to call syscall.Syscall correctly.
+// As of Go 1.6 the compiler handles that case automatically.
+// The uses and definition of use can be removed early in the Go 1.7 cycle.
//go:noescape
func use(p unsafe.Pointer)
--- /dev/null
+// errorcheck -0 -m -live
+
+// +build !windows
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test escape analysis and liveness inferred for syscall.Syscall-like functions.
+
+package p
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr"
+
+func g() {
+ var t int
+ f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: autotmp" "g &t does not escape"
+}
+
+func h() {
+ var v int
+ syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: autotmp" "h &v does not escape"
+}