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}}