blob: d4cfdc6ae5542df233ae5c1f51d9dd94a1290d68 [file] [log] [blame]
// Copyright (C) 2016 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 annotate
import (
"encoding/base64"
"fmt"
"io"
"sort"
"android.googlesource.com/platform/tools/gpu/api/semantic"
"android.googlesource.com/platform/tools/gpu/api/semantic/printer"
"android.googlesource.com/platform/tools/gpu/api/snippets"
"android.googlesource.com/platform/tools/gpu/framework/binary/cyclic"
"android.googlesource.com/platform/tools/gpu/framework/binary/vle"
)
// Analsyis is a map from atom name to snippet table.
type Analysis map[string]*SnippetTable
// Annotate visits the compiled API file and produces an Analysis object
// which contains all the static analysis snippets added to parameters
// and globals (state variables).
func Annotate(compiled *semantic.API) Analysis {
analysis := make(Analysis)
for _, f := range compiled.Functions {
analysis.function(f, compiled.Globals)
}
return analysis
}
func (analysis Analysis) function(f *semantic.Function, globals []*semantic.Global) {
defer func() {
if err := recover(); err != nil {
panic(fmt.Errorf("Panic raised: %v\n\nFunction:%v", err, printer.New().WriteFunction(f)))
}
}()
a := New()
// Note the globals are deliberately scoped within the function for the
// analysis. This is so that we can learn about the globals in the
// context of a specific atom.
a.beginScope()
vars := []semantic.Expression{}
for _, g := range globals {
vars = append(vars, g)
}
for _, p := range f.FullParameters {
vars = append(vars, p)
}
for _, v := range vars {
a.declare(v)
}
a.scoped(func() { a.visitStatement(f.Block) })
var table *SnippetTable
for _, v := range vars {
loc := a.locate(v)
if !loc.isEmpty() {
p := path(v)
if table == nil {
table = &SnippetTable{}
}
loc.getTable(p, table)
if len(*table) != 0 {
analysis[f.Name()] = table
}
}
}
}
// Globals builds a single snippet table which contains all the snippets for
// global paths. When the same global path is inferred for multiple atoms the
// snippets for that path are merged. The snippet table is produced in a
// consistent order.
func (a Analysis) Globals() SnippetTable {
// Sort atom names so that snippet order is consistent.
names := sort.StringSlice{}
for name := range a {
names = append(names, name)
}
names.Sort()
indexByPathStr := make(map[string]int)
paths := sort.StringSlice{}
var table SnippetTable
i := 0
for _, name := range names {
g := a[name].Globals()
for _, entry := range g {
pathStr := fmt.Sprint(entry.path)
if prevIndex, ok := indexByPathStr[pathStr]; ok {
// Merge with existing entry (order preserving)
table[prevIndex].snippets.AddSnippets(entry.snippets)
} else {
// New entry
paths = append(paths, pathStr)
table = append(table, entry)
indexByPathStr[pathStr] = i
i++
}
}
}
// Sort the tables into path order so that the table is in consistent order.
paths.Sort()
var sortedTable SnippetTable
sortedTable = make(SnippetTable, len(table))
for i, p := range paths {
sortedTable[i] = table[indexByPathStr[p]]
}
return sortedTable
}
// GlobalsGroups builds a snippet global analysis object which is a slice
// of kindred snippets. It contains all the snippets for global paths.
// When the same global path is inferred for multiple atoms the snippets
// for that path are merged. Each path has the snippets grouped by snippet
// type and binary encodable. The groups are in a consistent order.
func (a Analysis) GlobalsGrouped() GlobalAnalysis {
g := a.Globals()
return GlobalAnalysis(g.KindredGroups(snippets.GlobalsTypename))
}
// Print outputs the analysis object to out in human readable form.
// Atom names are sorted lexiographically.
func (a Analysis) Print(out io.Writer) {
names := sort.StringSlice{}
for name := range a {
names = append(names, name)
}
names.Sort()
for _, name := range names {
table := a[name]
fmt.Fprintf(out, "===================================== %s\n", name)
fmt.Fprint(out, table)
}
}
// Base64 outputs the analysis object using binary encoding and base64 encoding.
// Atom names are sorted lexiographically. Base64 encoding is used so that
// the binary file can be embedded in the library using the embed tool.
func (a Analysis) Base64(out io.Writer) error {
names := sort.StringSlice{}
for name := range a {
names = append(names, name)
}
names.Sort()
b64 := base64.NewEncoder(base64.StdEncoding, out)
e := cyclic.Encoder(vle.Writer(b64))
for _, name := range names {
table := a[name].NonGlobals()
groups := table.KindredGroups(name)
atomSnips := &snippets.AtomSnippets{AtomName: name, Snippets: groups}
e.Variant(atomSnips)
}
if e.Error() != nil {
return e.Error()
}
return b64.Close()
}
// GlobalAnalysis is kindred grouped table of snippets with paths that apply
// to the global state. It is used to provide snippets for the API state
// object and ultimately the state view in the UI.
type GlobalAnalysis []snippets.KindredSnippets
// Base64 outputs the analysis object using binary encoding and base64 encoding.
// Output is in a consistent order. Base64 encoding is used so that the binary
// file can be embedded in the library using the embed tool.
func (a GlobalAnalysis) Base64(out io.Writer) error {
b64 := base64.NewEncoder(base64.StdEncoding, out)
e := cyclic.Encoder(vle.Writer(b64))
for _, snip := range a {
e.Variant(snip)
}
if e.Error() != nil {
return e.Error()
}
return b64.Close()
}