cmd/compile: redo IsRuntimePkg/IsReflectPkg predicate
Currently, the types package has IsRuntimePkg and IsReflectPkg
predicates for testing if a Pkg is the runtime or reflect packages.
IsRuntimePkg returns "true" for any "CompilingRuntime" package, which
includes all of the packages imported by the runtime. This isn't
inherently wrong, except that all but one use of it is of the form "is
this Sym a specific runtime.X symbol?" for which we clearly only want
the package "runtime" itself. IsRuntimePkg was introduced (as
isRuntime) in CL 37538 as part of separating the real runtime package
from the compiler built-in fake runtime package. As of that CL, the
"runtime" package couldn't import any other packages, so this was
adequate at the time.
We could fix this by just changing the implementation of IsRuntimePkg,
but the meaning of this API is clearly somewhat ambiguous. Instead, we
replace it with a new RuntimeSymName function that returns the name of
a symbol if it's in package "runtime", or "" if not. This is what
every call site (except one) actually wants, which lets us simplify
the callers, and also more clearly addresses the ambiguity between
package "runtime" and the general concept of a runtime package.
IsReflectPkg doesn't have the same issue of ambiguity, but it
parallels IsRuntimePkg and is used in the same way, so we replace it
with a new ReflectSymName for consistency.
Change-Id: If3a81d7d11732a9ab2cac9488d17508415cfb597
Reviewed-on: https://go-review.googlesource.com/c/go/+/521696
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-07 14:16:30 -06:00
|
|
|
// errorcheck -+ -p=runtime
|
2016-10-10 14:46:28 -06:00
|
|
|
|
|
|
|
// Copyright 2016 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 go:nowritebarrier and related directives.
|
cmd/compile: redo IsRuntimePkg/IsReflectPkg predicate
Currently, the types package has IsRuntimePkg and IsReflectPkg
predicates for testing if a Pkg is the runtime or reflect packages.
IsRuntimePkg returns "true" for any "CompilingRuntime" package, which
includes all of the packages imported by the runtime. This isn't
inherently wrong, except that all but one use of it is of the form "is
this Sym a specific runtime.X symbol?" for which we clearly only want
the package "runtime" itself. IsRuntimePkg was introduced (as
isRuntime) in CL 37538 as part of separating the real runtime package
from the compiler built-in fake runtime package. As of that CL, the
"runtime" package couldn't import any other packages, so this was
adequate at the time.
We could fix this by just changing the implementation of IsRuntimePkg,
but the meaning of this API is clearly somewhat ambiguous. Instead, we
replace it with a new RuntimeSymName function that returns the name of
a symbol if it's in package "runtime", or "" if not. This is what
every call site (except one) actually wants, which lets us simplify
the callers, and also more clearly addresses the ambiguity between
package "runtime" and the general concept of a runtime package.
IsReflectPkg doesn't have the same issue of ambiguity, but it
parallels IsRuntimePkg and is used in the same way, so we replace it
with a new ReflectSymName for consistency.
Change-Id: If3a81d7d11732a9ab2cac9488d17508415cfb597
Reviewed-on: https://go-review.googlesource.com/c/go/+/521696
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-07 14:16:30 -06:00
|
|
|
// This must appear to be in package runtime so the compiler
|
|
|
|
// recognizes "systemstack".
|
2016-10-10 14:46:28 -06:00
|
|
|
|
cmd/compile: redo IsRuntimePkg/IsReflectPkg predicate
Currently, the types package has IsRuntimePkg and IsReflectPkg
predicates for testing if a Pkg is the runtime or reflect packages.
IsRuntimePkg returns "true" for any "CompilingRuntime" package, which
includes all of the packages imported by the runtime. This isn't
inherently wrong, except that all but one use of it is of the form "is
this Sym a specific runtime.X symbol?" for which we clearly only want
the package "runtime" itself. IsRuntimePkg was introduced (as
isRuntime) in CL 37538 as part of separating the real runtime package
from the compiler built-in fake runtime package. As of that CL, the
"runtime" package couldn't import any other packages, so this was
adequate at the time.
We could fix this by just changing the implementation of IsRuntimePkg,
but the meaning of this API is clearly somewhat ambiguous. Instead, we
replace it with a new RuntimeSymName function that returns the name of
a symbol if it's in package "runtime", or "" if not. This is what
every call site (except one) actually wants, which lets us simplify
the callers, and also more clearly addresses the ambiguity between
package "runtime" and the general concept of a runtime package.
IsReflectPkg doesn't have the same issue of ambiguity, but it
parallels IsRuntimePkg and is used in the same way, so we replace it
with a new ReflectSymName for consistency.
Change-Id: If3a81d7d11732a9ab2cac9488d17508415cfb597
Reviewed-on: https://go-review.googlesource.com/c/go/+/521696
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-07 14:16:30 -06:00
|
|
|
package runtime
|
2016-10-10 14:46:28 -06:00
|
|
|
|
|
|
|
type t struct {
|
|
|
|
f *t
|
|
|
|
}
|
|
|
|
|
|
|
|
var x t
|
|
|
|
var y *t
|
|
|
|
|
|
|
|
//go:nowritebarrier
|
|
|
|
func a1() {
|
|
|
|
x.f = y // ERROR "write barrier prohibited"
|
|
|
|
a2() // no error
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
func a2() {
|
|
|
|
x.f = y
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:nowritebarrierrec
|
|
|
|
func b1() {
|
|
|
|
b2()
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
func b2() {
|
|
|
|
x.f = y // ERROR "write barrier prohibited by caller"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test recursive cycles through nowritebarrierrec and yeswritebarrierrec.
|
|
|
|
|
|
|
|
//go:nowritebarrierrec
|
|
|
|
func c1() {
|
|
|
|
c2()
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:yeswritebarrierrec
|
|
|
|
func c2() {
|
|
|
|
c3()
|
|
|
|
}
|
|
|
|
|
|
|
|
func c3() {
|
|
|
|
x.f = y
|
|
|
|
c4()
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:nowritebarrierrec
|
|
|
|
func c4() {
|
|
|
|
c2()
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:nowritebarrierrec
|
|
|
|
func d1() {
|
|
|
|
d2()
|
|
|
|
}
|
|
|
|
|
|
|
|
func d2() {
|
|
|
|
d3()
|
|
|
|
}
|
|
|
|
|
2020-03-31 21:24:05 -06:00
|
|
|
//go:noinline
|
2016-10-10 14:46:28 -06:00
|
|
|
func d3() {
|
|
|
|
x.f = y // ERROR "write barrier prohibited by caller"
|
|
|
|
d4()
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:yeswritebarrierrec
|
|
|
|
func d4() {
|
|
|
|
d2()
|
|
|
|
}
|
cmd/compile: improve coverage of nowritebarrierrec check
The current go:nowritebarrierrec checker has two problems that limit
its coverage:
1. It doesn't understand that systemstack calls its argument, which
means there are several cases where we fail to detect prohibited write
barriers.
2. It only observes calls in the AST, so calls constructed during
lowering by SSA aren't followed.
This CL completely rewrites this checker to address these issues.
The current checker runs entirely after walk and uses visitBottomUp,
which introduces several problems for checking across systemstack.
First, visitBottomUp itself doesn't understand systemstack calls, so
the callee may be ordered after the caller, causing the checker to
fail to propagate constraints. Second, many systemstack calls are
passed a closure, which is quite difficult to resolve back to the
function definition after transformclosure and walk have run. Third,
visitBottomUp works exclusively on the AST, so it can't observe calls
created by SSA.
To address these problems, this commit splits the check into two
phases and rewrites it to use a call graph generated during SSA
lowering. The first phase runs before transformclosure/walk and simply
records systemstack arguments when they're easy to get. Then, it
modifies genssa to record static call edges at the point where we're
lowering to Progs (which is the latest point at which position
information is conveniently available). Finally, the second phase runs
after all functions have been lowered and uses a direct BFS walk of
the call graph (combining systemstack calls with static calls) to find
prohibited write barriers and construct nice error messages.
Fixes #22384.
For #22460.
Change-Id: I39668f7f2366ab3c1ab1a71eaf25484d25349540
Reviewed-on: https://go-review.googlesource.com/72773
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-10-22 14:36:27 -06:00
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
func systemstack(func()) {}
|
|
|
|
|
|
|
|
//go:nowritebarrierrec
|
|
|
|
func e1() {
|
|
|
|
systemstack(e2)
|
|
|
|
systemstack(func() {
|
|
|
|
x.f = y // ERROR "write barrier prohibited by caller"
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func e2() {
|
|
|
|
x.f = y // ERROR "write barrier prohibited by caller"
|
|
|
|
}
|