blob: 04680a5bb5ae2296fb93faf46bd8dd3c26a71274 [file] [log] [blame]
// Copyright (C) 2015 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 validate
import (
"android.googlesource.com/platform/tools/gpu/api/apic/validate/limits"
"android.googlesource.com/platform/tools/gpu/api/semantic"
"android.googlesource.com/platform/tools/gpu/parse"
)
// noUnreachables checks there are no unreachable blocks.
func noUnreachables(api *semantic.API) []parse.Error {
ctx := context{
locals: make(map[*semantic.Local]limits.Limits),
parameters: make(map[*semantic.Parameter]limits.Limits),
errors: &errors{},
}
semantic.Visit(api, ctx.traverse)
return *ctx.errors
}
func (ctx *context) traverse(n semantic.Node) {
switch n := n.(type) {
case *semantic.Function:
ctx := ctx.clone()
for _, p := range n.FullParameters {
ctx.parameters[p] = limits.Unbound(p.ExpressionType())
}
if n.Block != nil {
semantic.Visit(n.Block, ctx.traverse)
}
case *semantic.Assert:
*ctx = *ctx.setTrue(n.Condition)
case *semantic.DeclareLocal:
ctx.locals[n.Local] = ctx.limits(n.Local.Value)
case *semantic.Branch:
limit := ctx.limits(n.Condition)
if n.True != nil && len(n.True.Statements) > 0 {
if limit == limits.False {
ctx.errors.add(n.True.AST.CST, "Unreachable block")
} else {
semantic.Visit(n.True, ctx.clone().setTrue(n.Condition).traverse)
}
}
if n.False != nil && len(n.False.Statements) > 0 {
if limit == limits.True {
ctx.errors.add(n.False.AST.CST, "Unreachable block")
} else {
semantic.Visit(n.False, ctx.clone().setFalse(n.Condition).traverse)
}
}
default:
semantic.Visit(n, ctx.traverse)
}
}
type errors []parse.Error
func (l *errors) add(at parse.Fragment, msg string) {
(*l) = append(*l, parse.Error{At: at, Message: msg})
}
type context struct {
locals map[*semantic.Local]limits.Limits
parameters map[*semantic.Parameter]limits.Limits
errors *errors
}
func (ctx *context) clone() *context {
c := context{
locals: make(map[*semantic.Local]limits.Limits),
parameters: make(map[*semantic.Parameter]limits.Limits),
errors: ctx.errors,
}
for local, limit := range ctx.locals {
c.locals[local] = limit
}
for parameter, limit := range ctx.parameters {
c.parameters[parameter] = limit
}
return &c
}
func (ctx *context) limits(n semantic.Expression) limits.Limits {
switch n := n.(type) {
case *semantic.Local:
return ctx.locals[n]
case *semantic.Parameter:
return ctx.parameters[n]
case semantic.BoolValue:
if n {
return limits.True
} else {
return limits.False
}
case semantic.Uint16Value:
return limits.Uint(uint64(n))
case semantic.Uint32Value:
return limits.Uint(uint64(n))
case *semantic.BinaryOp:
lhs, rhs := ctx.limits(n.LHS), ctx.limits(n.RHS)
if lhs != nil && rhs != nil {
return lhs.Binary(n.Operator, rhs)
}
case *semantic.UnaryOp:
expr := ctx.limits(n.Expression)
if expr != nil {
return expr.Unary(n.Operator)
}
}
return nil
}
func (ctx *context) setTrue(n semantic.Expression) *context {
switch n := n.(type) {
case *semantic.Local:
ctx.locals[n] = limits.True
case *semantic.Parameter:
ctx.parameters[n] = limits.True
case *semantic.BinaryOp:
switch n.Operator {
case "&&":
ctx.setTrue(n.LHS)
ctx.setTrue(n.RHS)
}
case *semantic.UnaryOp:
switch n.Operator {
case "!":
ctx.setFalse(n.Expression)
}
case semantic.BoolValue:
if n != true {
panic("Asserting true value is false")
}
}
return ctx
}
func (ctx *context) setFalse(n semantic.Expression) *context {
switch n := n.(type) {
case *semantic.Local:
ctx.locals[n] = limits.False
case *semantic.Parameter:
ctx.parameters[n] = limits.False
case *semantic.BinaryOp:
switch n.Operator {
case "||":
ctx.setFalse(n.LHS)
ctx.setFalse(n.RHS)
}
case *semantic.UnaryOp:
switch n.Operator {
case "!":
ctx.setTrue(n.Expression)
}
case semantic.BoolValue:
if n != false {
panic("Asserting false value is true")
}
}
return ctx
}