Commit fd178d6a authored by Russ Cox's avatar Russ Cox

cmd/gc: add way to specify 'noescape' for extern funcs

A new comment directive //go:noescape instructs the compiler
that the following external (no body) func declaration should be
treated as if none of its arguments escape to the heap.

Fixes #4099.

R=golang-dev, dave, minux.ma, daniel.morsing, remyoudompheng, adg, agl, iant
CC=golang-dev
https://golang.org/cl/7289048
parent f1c409b9
......@@ -25,6 +25,8 @@ other packages. It is therefore not necessary when compiling client C of
package P to read the files of P's dependencies, only the compiled output
of P.
Command Line
Usage:
go tool 6g [flags] file...
The specified files must be Go source files and all part of the same package.
......@@ -48,7 +50,7 @@ Flags:
disable optimizations
-S
write assembly language text to standard output (code only)
-SS
-S -S
write assembly language text to standard output (code and data)
-u
disallow importing packages not marked as safe
......@@ -60,5 +62,28 @@ Flags:
There are also a number of debugging flags; run the command with no arguments
to get a usage message.
Compiler Directives
The compiler accepts two compiler directives in the form of // comments at the
beginning of a line. To distinguish them from non-directive comments, the directives
require no space between the slashes and the name of the directive. However, since
they are comments, tools unaware of the directive convention or of a particular
directive can skip over a directive like any other comment.
//line path/to/file:linenumber
The //line directive specifies that the source line that follows should be recorded
as having come from the given file path and line number. Successive lines are
recorded using increasing line numbers, until the next directive. This directive
typically appears in machine-generated code, so that compilers and debuggers
will show lines in the original input to the generator.
//go:noescape
The //go:noescape directive specifies that the next declaration in the file, which
must be a func without a body (meaning that it has an implementation not written
in Go) does not allow any of the pointers passed as arguments to escape into the
heap or into the values returned from the function. This information can be used as
during the compiler's escape analysis of Go code calling the function.
*/
package documentation
......@@ -330,7 +330,10 @@ escfunc(EscState *e, Node *func)
case PPARAM:
if(ll->n->type && !haspointers(ll->n->type))
break;
ll->n->esc = EscNone; // prime for escflood later
if(curfn->nbody == nil && !curfn->noescape)
ll->n->esc = EscHeap;
else
ll->n->esc = EscNone; // prime for escflood later
e->noesc = list(e->noesc, ll->n);
ll->n->escloopdepth = 1;
break;
......@@ -1109,13 +1112,21 @@ esctag(EscState *e, Node *func)
{
Node *savefn;
NodeList *ll;
Type *t;
USED(e);
func->esc = EscFuncTagged;
// External functions must be assumed unsafe.
if(func->nbody == nil)
// External functions are assumed unsafe,
// unless //go:noescape is given before the declaration.
if(func->nbody == nil) {
if(func->noescape) {
for(t=getinargx(func->type)->type; t; t=t->down)
if(haspointers(t->type))
t->note = mktag(EscNone);
}
return;
}
savefn = curfn;
curfn = func;
......
......@@ -253,6 +253,7 @@ struct Node
uchar colas; // OAS resulting from :=
uchar diag; // already printed error about this
uchar esc; // EscXXX
uchar noescape; // func arguments do not escape
uchar funcdepth;
uchar builtin; // built-in name, like len or close
uchar walkdef;
......@@ -943,6 +944,7 @@ EXTERN int compiling_wrappers;
EXTERN int pure_go;
EXTERN int flag_race;
EXTERN int flag_largemodel;
EXTERN int noescape;
EXTERN int nointerface;
EXTERN int fieldtrack_enabled;
......
......@@ -1277,8 +1277,11 @@ xfndcl:
$$ = $2;
if($$ == N)
break;
if(noescape && $3 != nil)
yyerror("can only use //go:noescape with external func implementations");
$$->nbody = $3;
$$->endlineno = lineno;
$$->noescape = noescape;
funcbody($$);
}
......@@ -1462,6 +1465,7 @@ xdcl_list:
if(nsyntaxerrors == 0)
testdclstack();
nointerface = 0;
noescape = 0;
}
vardcl_list:
......
......@@ -1436,7 +1436,7 @@ getlinepragma(void)
Hist *h;
c = getr();
if(c == 'g' && fieldtrack_enabled)
if(c == 'g')
goto go;
if(c != 'l')
goto out;
......@@ -1491,18 +1491,32 @@ getlinepragma(void)
goto out;
go:
for(i=1; i<11; i++) {
c = getr();
if(c != "go:nointerface"[i])
goto out;
}
nointerface = 1;
cp = lexbuf;
ep = lexbuf+sizeof(lexbuf)-5;
*cp++ = 'g'; // already read
for(;;) {
c = getr();
if(c == EOF || c == '\n')
if(c == EOF || c >= Runeself)
goto out;
if(c == '\n')
break;
if(cp < ep)
*cp++ = c;
}
*cp = 0;
ep = strchr(lexbuf, ' ');
if(ep != nil)
*ep = 0;
if(strcmp(lexbuf, "go:nointerface") == 0 && fieldtrack_enabled) {
nointerface = 1;
goto out;
}
if(strcmp(lexbuf, "go:noescape") == 0) {
noescape = 1;
goto out;
}
out:
return c;
}
......
This diff is collapsed.
......@@ -1274,3 +1274,29 @@ func foo140() interface{} {
T: t,
}
}
//go:noescape
func F1([]byte)
func F2([]byte)
//go:noescape
func F3(x []byte) // ERROR "F3 x does not escape"
func F4(x []byte)
func G() {
var buf1 [10]byte
F1(buf1[:]) // ERROR "buf1 does not escape"
var buf2 [10]byte // ERROR "moved to heap: buf2"
F2(buf2[:]) // ERROR "buf2 escapes to heap"
var buf3 [10]byte
F3(buf3[:]) // ERROR "buf3 does not escape"
var buf4 [10]byte // ERROR "moved to heap: buf4"
F4(buf4[:]) // ERROR "buf4 escapes to heap"
}
// errorcheck -0 -m
// Copyright 2013 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.
// Check go:noescape annotations.
package p
// The noescape comment only applies to the next func,
// which must not have a body.
//go:noescape
func F1([]byte)
func F2([]byte)
func G() {
var buf1 [10]byte
F1(buf1[:]) // ERROR "buf1 does not escape"
var buf2 [10]byte // ERROR "moved to heap: buf2"
F2(buf2[:]) // ERROR "buf2 escapes to heap"
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment