cmd/compile, syscall: add //go:uintptrescapes comment, and use it

This new comment can be used to declare that the uintptr arguments to a
function may be converted from pointers, and that those pointers should
be considered to escape. This is used for the Call methods in
dll_windows.go that take uintptr arguments, because they call Syscall.

We can't treat these functions as we do syscall.Syscall, because unlike
Syscall they may cause the stack to grow. For Syscall we can assume that
stack arguments can remain on the stack, but for these functions we need
them to escape.

Fixes #16035.

Change-Id: Ia0e5b4068c04f8d303d95ab9ea394939f1f57454
Reviewed-on: https://go-review.googlesource.com/24551
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Ian Lance Taylor 2016-06-28 14:19:27 -07:00
parent 820e30f5b0
commit bbe5da4260
8 changed files with 259 additions and 12 deletions

View file

@ -1551,10 +1551,12 @@ func esccall(e *EscState, n *Node, up *Node) {
}
var src *Node
note := ""
i := 0
lls := ll.Slice()
for t, it := IterFields(fntype.Params()); i < len(lls); i++ {
src = lls[i]
note = t.Note
if t.Isddd && !n.Isddd {
// Introduce ODDDARG node to represent ... allocation.
src = Nod(ODDDARG, nil, nil)
@ -1566,7 +1568,7 @@ func esccall(e *EscState, n *Node, up *Node) {
}
if haspointers(t.Type) {
if escassignfromtag(e, t.Note, nE.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
if escassignfromtag(e, note, nE.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
a := src
for a.Op == OCONVNOP {
a = a.Left
@ -1596,14 +1598,24 @@ func esccall(e *EscState, n *Node, up *Node) {
// This occurs when function parameter type Isddd and n not Isddd
break
}
if note == uintptrEscapesTag {
escassignSinkNilWhy(e, src, src, "escaping uintptr")
}
t = it.Next()
}
// Store arguments into slice for ... arg.
for ; i < len(lls); i++ {
if Debug['m'] > 3 {
fmt.Printf("%v::esccall:: ... <- %v\n", linestr(lineno), Nconv(lls[i], FmtShort))
}
escassignNilWhy(e, src, lls[i], "arg to ...") // args to slice
if note == uintptrEscapesTag {
escassignSinkNilWhy(e, src, lls[i], "arg to uintptrescapes ...")
} else {
escassignNilWhy(e, src, lls[i], "arg to ...")
}
}
}
@ -1963,9 +1975,20 @@ recurse:
// lets us take the address below to get a *string.
var unsafeUintptrTag = "unsafe-uintptr"
// This special tag is applied to uintptr parameters of functions
// marked go:uintptrescapes.
const uintptrEscapesTag = "uintptr-escapes"
func esctag(e *EscState, func_ *Node) {
func_.Esc = EscFuncTagged
name := func(s *Sym, narg int) string {
if s != nil {
return s.Name
}
return fmt.Sprintf("arg#%d", narg)
}
// External functions are assumed unsafe,
// unless //go:noescape is given before the declaration.
if func_.Nbody.Len() == 0 {
@ -1988,13 +2011,7 @@ func esctag(e *EscState, func_ *Node) {
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(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name)
Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name(t.Sym, narg))
}
t.Note = unsafeUintptrTag
}
@ -2003,6 +2020,27 @@ func esctag(e *EscState, func_ *Node) {
return
}
if func_.Func.Pragma&UintptrEscapes != 0 {
narg := 0
for _, t := range func_.Type.Params().Fields().Slice() {
narg++
if t.Type.Etype == TUINTPTR {
if Debug['m'] != 0 {
Warnl(func_.Lineno, "%v marking %v as escaping uintptr", funcSym(func_), name(t.Sym, narg))
}
t.Note = uintptrEscapesTag
}
if t.Isddd && t.Type.Elem().Etype == TUINTPTR {
// final argument is ...uintptr.
if Debug['m'] != 0 {
Warnl(func_.Lineno, "%v marking %v as escaping ...uintptr", funcSym(func_), name(t.Sym, narg))
}
t.Note = uintptrEscapesTag
}
}
}
savefn := Curfn
Curfn = func_
@ -2015,7 +2053,9 @@ func esctag(e *EscState, func_ *Node) {
case EscNone, // not touched by escflood
EscReturn:
if haspointers(ln.Type) { // don't bother tagging for scalars
ln.Name.Param.Field.Note = mktag(int(ln.Esc))
if ln.Name.Param.Field.Note != uintptrEscapesTag {
ln.Name.Param.Field.Note = mktag(int(ln.Esc))
}
}
case EscHeap, // touched by escflood, moved to heap