diff options
Diffstat (limited to 'src/cmd/compile/internal/noder')
-rw-r--r-- | src/cmd/compile/internal/noder/decl.go | 22 | ||||
-rw-r--r-- | src/cmd/compile/internal/noder/lex.go | 19 | ||||
-rw-r--r-- | src/cmd/compile/internal/noder/noder.go | 3 | ||||
-rw-r--r-- | src/cmd/compile/internal/noder/writer.go | 16 |
4 files changed, 46 insertions, 14 deletions
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go index f985648c667..91a90d9e090 100644 --- a/src/cmd/compile/internal/noder/decl.go +++ b/src/cmd/compile/internal/noder/decl.go @@ -125,8 +125,26 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) { } } - if decl.Body != nil && fn.Pragma&ir.Noescape != 0 { - base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations") + if decl.Body != nil { + if fn.Pragma&ir.Noescape != 0 { + base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations") + } + if (fn.Pragma&ir.UintptrKeepAlive != 0 && fn.Pragma&ir.UintptrEscapes == 0) && fn.Pragma&ir.Nosplit == 0 { + // Stack growth can't handle uintptr arguments that may + // be pointers (as we don't know which are pointers + // when creating the stack map). Thus uintptrkeepalive + // functions (and all transitive callees) must be + // nosplit. + // + // N.B. uintptrescapes implies uintptrkeepalive but it + // is OK since the arguments must escape to the heap. + // + // TODO(prattmic): Add recursive nosplit check of callees. + // TODO(prattmic): Functions with no body (i.e., + // assembly) must also be nosplit, but we can't check + // that here. + base.ErrorfAt(fn.Pos(), "go:uintptrkeepalive requires go:nosplit") + } } if decl.Name.Value == "init" && decl.Recv == nil { diff --git a/src/cmd/compile/internal/noder/lex.go b/src/cmd/compile/internal/noder/lex.go index 66a56a50ec8..cef0f082ca9 100644 --- a/src/cmd/compile/internal/noder/lex.go +++ b/src/cmd/compile/internal/noder/lex.go @@ -30,6 +30,7 @@ const ( ir.NoCheckPtr | ir.RegisterParams | // TODO(register args) remove after register abi is working ir.CgoUnsafeArgs | + ir.UintptrKeepAlive | ir.UintptrEscapes | ir.Systemstack | ir.Nowritebarrier | @@ -67,19 +68,13 @@ func pragmaFlag(verb string) ir.PragmaFlag { return ir.Yeswritebarrierrec case "go:cgo_unsafe_args": return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968) + case "go:uintptrkeepalive": + return ir.UintptrKeepAlive case "go:uintptrescapes": - // For the next function declared in the file - // any uintptr arguments may be pointer values - // converted to uintptr. This directive - // ensures that the referenced allocated - // object, if any, is retained and not moved - // until the call completes, even though from - // the types alone it would appear that the - // object is no longer needed during the - // call. The conversion to uintptr must appear - // in the argument list. - // Used in syscall/dll_windows.go. - return ir.UintptrEscapes + // This directive extends //go:uintptrkeepalive by forcing + // uintptr arguments to escape to the heap, which makes stack + // growth safe. + return ir.UintptrEscapes | ir.UintptrKeepAlive // implies UintptrKeepAlive case "go:registerparams": // TODO(register args) remove after register abi is working return ir.RegisterParams case "go:notinheap": diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index cc5610acda9..9a42b5afd17 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -340,6 +340,9 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 { p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)}) } + if flag == ir.UintptrKeepAlive && !base.Flag.Std { + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is only allowed in the standard library", verb)}) + } if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std { p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)}) } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index c5c346b7842..0fb162d381b 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -742,6 +742,22 @@ func (w *writer) funcExt(obj *types2.Func) { if pragma&ir.Noescape != 0 { w.p.errorf(decl, "can only use //go:noescape with external func implementations") } + if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 { + // Stack growth can't handle uintptr arguments that may + // be pointers (as we don't know which are pointers + // when creating the stack map). Thus uintptrkeepalive + // functions (and all transitive callees) must be + // nosplit. + // + // N.B. uintptrescapes implies uintptrkeepalive but it + // is OK since the arguments must escape to the heap. + // + // TODO(prattmic): Add recursive nosplit check of callees. + // TODO(prattmic): Functions with no body (i.e., + // assembly) must also be nosplit, but we can't check + // that here. + w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit") + } } else { if base.Flag.Complete || decl.Name.Value == "init" { // Linknamed functions are allowed to have no body. Hopefully |