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