Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: does not check go:writebarrierrec for print functions #34014

Open
ianlancetaylor opened this issue Sep 2, 2019 · 4 comments
Open

cmd/compile: does not check go:writebarrierrec for print functions #34014

ianlancetaylor opened this issue Sep 2, 2019 · 4 comments
Milestone

Comments

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 2, 2019

The runtime function sighandler is marked go:nowritebarrierrec. It calls print which cmd/compile turns into calls to printstring and friends. However, if I modify printstring to force a write barrier, no error is issued when compiling the runtime package. If I then mark printstring as go:nowritebarrierrec, I do get an error. My conclusion from this is that cmd/compile does not apply go:nowritebarrierrec to the calls generated by calling the print function.

I believe this is because in (*nowritebarrierrecChecker).check the printstring functions are never added to the symToFunc map.

CC @aclements

@ianlancetaylor ianlancetaylor added this to the Go1.14 milestone Sep 2, 2019
@odeke-em
Copy link
Member

@odeke-em odeke-em commented Sep 26, 2019

Thanks for this report @ianlancetaylor! I think this issue might show a symptom that go:nowritebarrierrec checks don't seem to be checked past the first degree of call invocation and also that inlining interacts improperly with those checks.

With degree 1

This is similar to manually adding //go:nowritebarrierrec to printstring

diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index 6a8b5b7ace..46f4b27f3f 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -229,6 +229,20 @@ func clearSignalHandlers() {
 	}
 }
 
+//go:nowritebarrierrec
+func f1(x *[]byte, y *[]byte) {
+    foo(x, y)
+}
+
+func f2() {
+    var x, y []byte
+    foo(&x, &y)
+}
+
+func foo(x *[]byte, y *[]byte) {
+    *x = *y
+}
+

and on running go get . properly prints out the error

$ GO111MODULE=off go get .
# runtime
./signal_unix.go:234:8: write barrier prohibited
./signal_unix.go:234:8: write barrier prohibited by caller; f1

With degree 2 of connection, inlining enabled

diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index 6a8b5b7ace..4aaf4cef87 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -229,6 +229,21 @@ func clearSignalHandlers() {
 	}
 }
 
+//go:nowritebarrierrec
+func f1(x *[]byte, y *[]byte) []byte {
+    return f2()
+}
+
+func f2() []byte {
+    var x, y []byte
+    return foo(&x, &y)
+}
+
+func foo(x *[]byte, y *[]byte) []byte{
+    *x = *y
+    return *x
+}
+

when run doesn't print any error.

 GO111MODULE=off go get .

With degree 2 and disabled inlining

diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index 6a8b5b7ace..4aaf4cef87 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -229,6 +229,21 @@ func clearSignalHandlers() {
 	}
 }
 
+//go:nowritebarrierrec
+func f1(x *[]byte, y *[]byte) []byte {
+    return f2()
+}
+
+func f2() []byte {
+    var x, y []byte
+    return foo(&x, &y)
+}
+
+func foo(x *[]byte, y *[]byte) []byte{
+    *x = *y
+    return *x
+}
+
$ GO111MODULE=off go get -gcflags='-l' .
# runtime
./signal_unix.go:243:8: write barrier prohibited by caller; foo
	/Users/emmanuelodeke/go/src/go.googlesource.com/go/src/runtime/signal_unix.go:239:15: called by f2
	/Users/emmanuelodeke/go/src/go.googlesource.com/go/src/runtime/signal_unix.go:234:14: called by f1

Please re-attempt your experiment with the -l compile flag and if you properly get the transitive error then we can be sure that the interaction between inlining and //go:nowriterbarrierrec is what's faulty.

@odeke-em
Copy link
Member

@odeke-em odeke-em commented Sep 26, 2019

My apologies, my analysis is totally unrelated to your problem @ianlancetaylor! Regardless of if I use -l or not and then set a write barrier in printstring, it'll not fail

diff --git a/src/runtime/print.go b/src/runtime/print.go
index e605eb34cb..29ea1a864b 100644
--- a/src/runtime/print.go
+++ b/src/runtime/print.go
@@ -238,7 +238,10 @@ func printpointer(p unsafe.Pointer) {
 	printhex(uint64(uintptr(p)))
 }
 
+var z string
+
 func printstring(s string) {
+	z = s
 	gwrite(bytes(s))
 }

Unless I also manually set go://nowritebarrierrec on printstring so this is definitely unrelated to inlining and it is in the cmd/compile code.

@aclements
Copy link
Member

@aclements aclements commented Sep 26, 2019

@odeke-em, just to close the loop on your examples, your "With degree 2 of connection, inlining enabled" example doesn't fail because there's actually no write barrier under f1. In particular, when foo gets inlined into f2, the compiler can optimize away the write barrier for the assignment.

@rsc rsc modified the milestones: Go1.14, Backlog Oct 9, 2019
@odeke-em
Copy link
Member

@odeke-em odeke-em commented Oct 16, 2019

Got it, thank you @aclements!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.