Add detection of read/write boundary in functions.

Change-Id: I51162fa894ea23c1638af54504ff5dc5a5be1f1e
diff --git a/api/apic/template/types.go b/api/apic/template/types.go
index 0b2b775..48bd08e 100644
--- a/api/apic/template/types.go
+++ b/api/apic/template/types.go
@@ -60,6 +60,7 @@
 		semantic.DeclareLocal{},
 		semantic.EnumEntry{},
 		semantic.Enum{},
+		semantic.Fence{},
 		semantic.Field{},
 		semantic.Function{},
 		semantic.Global{},
diff --git a/api/resolver/flow.go b/api/resolver/flow.go
new file mode 100644
index 0000000..a3f597d
--- /dev/null
+++ b/api/resolver/flow.go
@@ -0,0 +1,101 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package resolver
+
+import "android.googlesource.com/platform/tools/gpu/api/semantic"
+
+type fenceTracker struct {
+	ctx  *context
+	post bool
+}
+
+func addFence(ctx *context, block *semantic.Block) {
+	t := fenceTracker{ctx: ctx}
+	if !t.boundary(block) {
+		block.Statements = append(block.Statements, &semantic.Fence{})
+	}
+}
+
+func (t *fenceTracker) scan(n semantic.Node) {
+	switch n := n.(type) {
+	case *semantic.Block:
+		for i := 0; i < len(n.Statements); i++ {
+			s := n.Statements[i]
+			if t.boundary(s) {
+				// first post operation detected, insert fence marker here
+				n.Statements = append(n.Statements, nil)
+				copy(n.Statements[i+1:], n.Statements[i:])
+				n.Statements[i] = &semantic.Fence{}
+				// step past the newly inserted fence
+				i++
+				// Continue calling boundary() on later statements to detect post-after-pre errors.
+			}
+		}
+	case *semantic.Branch:
+		t.scan(n.Condition)
+		if t.boundary(n.True) {
+			t.ctx.errorf(n.True, "pre-post boundary in true condition")
+		}
+		if n.False != nil {
+			if t.boundary(n.False) {
+				t.ctx.errorf(n.False, "pre-post boundary in condition")
+			}
+		}
+	case *semantic.Select, *semantic.Switch, *semantic.Iteration:
+		ct := *t
+		semantic.Visit(n, t.scan)
+		if ct.post && !t.post {
+			t.post = true
+			t.ctx.errorf(n, "pre-post boundary in %T", n)
+		}
+	case *semantic.Copy:
+		semantic.Visit(n, t.scan)
+		if t.post {
+			t.ctx.errorf(n, "copy after fence")
+		}
+		t.post = true
+	case *semantic.Read:
+		semantic.Visit(n, t.scan)
+		if t.post {
+			t.ctx.errorf(n, "read after fence")
+		}
+	case *semantic.Unknown:
+		t.post = true
+	case *semantic.Write:
+		semantic.Visit(n, t.scan)
+		t.post = true
+	case *semantic.SliceIndex:
+		semantic.Visit(n, t.scan)
+		if t.post {
+			t.ctx.errorf(n, "slice index after fence")
+		}
+	case *semantic.SliceAssign:
+		semantic.Visit(n.To, t.scan)
+		t.scan(n.Value)
+		t.post = true
+	default:
+		semantic.Visit(n, t.scan)
+	}
+}
+
+func (t *fenceTracker) boundary(n semantic.Node) bool {
+	ct := *t
+	ct.scan(n)
+	if ct.post && !t.post {
+		t.post = true
+		return true
+	}
+	return false
+}
diff --git a/api/resolver/function.go b/api/resolver/function.go
index 6749c5b..bf7c082 100644
--- a/api/resolver/function.go
+++ b/api/resolver/function.go
@@ -96,6 +96,7 @@
 			}
 			out.Annotations = annotations(ctx, in.Annotations)
 			out.Block = block(ctx, in.Block, out)
+			addFence(ctx, out.Block)
 		})
 	}
 	if len(out.Docs) == 0 {
diff --git a/api/semantic/statement.go b/api/semantic/statement.go
index a127aec..5aedac8 100644
--- a/api/semantic/statement.go
+++ b/api/semantic/statement.go
@@ -94,3 +94,7 @@
 	Function *Function   // the function this statement returns from
 	Value    Expression  // the value to be returned
 }
+
+// Fence is a marker to indicate a safe point between the last read and the first write.
+// It can be used to generate safe forwarding calls.
+type Fence struct{}
diff --git a/api/semantic/visit.go b/api/semantic/visit.go
index 78e2ce4..8836b51 100644
--- a/api/semantic/visit.go
+++ b/api/semantic/visit.go
@@ -28,6 +28,9 @@
 	case *SliceIndex:
 		visitor(n.Slice)
 		visitor(n.Index)
+	case *SliceAssign:
+		visitor(n.To)
+		visitor(n.Value)
 	case *Assert:
 		visitor(n.Condition)
 	case *Assign:
@@ -111,6 +114,7 @@
 		for _, m := range n.Methods {
 			visitor(m)
 		}
+	case *Fence:
 	case *Field:
 		visitor(n.Type)
 		if n.Default != nil {
@@ -177,6 +181,7 @@
 		for _, c := range n.Choices {
 			visitor(c)
 		}
+	case StringValue:
 	case *Switch:
 		visitor(n.Value)
 		for _, c := range n.Cases {
diff --git a/gfxapi/templates/cpp_common.tmpl b/gfxapi/templates/cpp_common.tmpl
index 1c83c2f..f630e98 100644
--- a/gfxapi/templates/cpp_common.tmpl
+++ b/gfxapi/templates/cpp_common.tmpl
@@ -302,6 +302,7 @@
   {{else if IsRead      $}}read({{Macro "C++.Read" $.Slice}});
   {{else if IsWrite     $}}write({{Macro "C++.Read" $.Slice}});
   {{else if IsCopy      $}}copy({{Macro "C++.Read" $.Dst}}, {{Macro "C++.Read" $.Src}});
+  {{else if IsFence     $}}
   {{else                 }}{{Error "unsupported statement %T: %+v" $ $}}
   {{end}}
 {{end}}
diff --git a/gfxapi/templates/go_common.tmpl b/gfxapi/templates/go_common.tmpl
index f2fae84..cd2928c 100644
--- a/gfxapi/templates/go_common.tmpl
+++ b/gfxapi/templates/go_common.tmpl
@@ -300,6 +300,7 @@
   {{else if IsCall         $}}{{Macro "Go.Call" $}}
   {{else if IsRead         $}}
   {{else if IsWrite        $}}
+  {{else if IsFence        $}}
   {{else if IsBranch       $}}if {{Macro "Go.Read" $.Condition}} {
     {{Macro "Go.Block" $.True}}
     {{if len $.False.Statements}}