cmd/compile: enforce nowritebarrier in SSA compiler

Make sure we don't generate write barriers in runtime
code that is marked to forbid write barriers.

Implement the optimization that if we're writing a sliced
slice back to the location it came from, we don't need a
write barrier.

Fixes #14784

Change-Id: I04b6a3b2ac303c19817e932a36a3b006de103aaa
Reviewed-on: https://go-review.googlesource.com/20791
Reviewed-by: Austin Clements <austin@google.com>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index b0c7c5f..1d3c1f6 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -96,6 +96,14 @@
 	if fn.Func.Pragma&CgoUnsafeArgs != 0 {
 		s.cgoUnsafeArgs = true
 	}
+	if fn.Func.Pragma&Nowritebarrier != 0 {
+		s.noWB = true
+	}
+	defer func() {
+		if s.WBLineno != 0 {
+			fn.Func.WBLineno = s.WBLineno
+		}
+	}()
 	// TODO(khr): build config just once at the start of the compiler binary
 
 	ssaExp.log = printssa
@@ -271,6 +279,8 @@
 	returns []*Node
 
 	cgoUnsafeArgs bool
+	noWB          bool
+	WBLineno      int32 // line number of first write barrier. 0=no write barriers
 }
 
 type funcLine struct {
@@ -2780,6 +2790,13 @@
 	// } else {
 	//   *left = *right
 	// }
+
+	if s.noWB {
+		s.Fatalf("write barrier prohibited")
+	}
+	if s.WBLineno == 0 {
+		s.WBLineno = left.Line
+	}
 	bThen := s.f.NewBlock(ssa.BlockPlain)
 	bElse := s.f.NewBlock(ssa.BlockPlain)
 	bEnd := s.f.NewBlock(ssa.BlockPlain)
@@ -2823,6 +2840,12 @@
 	//   store pointer fields
 	// }
 
+	if s.noWB {
+		s.Fatalf("write barrier prohibited")
+	}
+	if s.WBLineno == 0 {
+		s.WBLineno = left.Line
+	}
 	s.storeTypeScalars(t, left, right)
 
 	bThen := s.f.NewBlock(ssa.BlockPlain)