blob: 00e35c16c668a24a929e66d07acfa26c7f3a1de7 [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 (
"bytes"
"fmt"
"android.googlesource.com/platform/tools/gpu/api/semantic"
"android.googlesource.com/platform/tools/gpu/api/snippets"
)
// Location is a static representation of an assignable location in the
// program. Static information flow is used to propagate snippets to
// locations. It is the conceit of static analysis, that the snippets are
// inferred at the locations.
type Location struct {
parent *Location // If nil then this location contains the equivalence set
snips snippets.Snippets // Snippets at this location
nested Nested // Decomposable
}
// alias aliases two locations. Aliasing allows information collected at
// different locations to be merged into a single equivalence set. All
// mutations always happen on the equivalence set leader.
func alias(left *Location, right *Location) error {
return left.leader().alias(right.leader())
}
// Make a new location with a snippet associated with it.
func newLocation(snippet snippets.Snippet) *Location {
l := &Location{}
l.addSnippet(snippet)
return l
}
// addSnippet adds a snippet to this location.
func (l *Location) addSnippet(s snippets.Snippet) {
l = l.leader()
l.snips.Add(s)
}
// getSnippets returns the snippets.
func (l *Location) getSnippets() snippets.Snippets {
l = l.leader()
return l.snips
}
// alias makes l and right refer to the same equivalence set. Snippets are
// merged if necessary.
func (l *Location) alias(right *Location) error {
l = l.leader()
r := right.leader()
if l == r {
return nil
}
if l.nested == nil {
l.nested = r.nested
} else {
l.nested.merge(r.nested)
}
if l.snips == nil {
l.snips = r.snips
} else {
l.snips.AddSnippets(r.snips)
}
r.parent = l
return nil
}
// leader returns the leader of the equivalence set for this location. The
// leader maybe this location. The path to the leader is shorten for all
// locations which are traversed to find the leader.
func (l *Location) leader() *Location {
var backtrace []**Location
loc := l
for ; loc.parent != nil; loc = loc.parent {
backtrace = append(backtrace, &loc.parent)
}
// Shorten the traversal for future calls (as in union find)
// Note backtrace[len(backtrace) - 1] already points to loc
for i := len(backtrace) - 2; i >= 0; i-- {
(*backtrace[i]) = loc
}
// Shorten the traversal for future kinds (as in union find)
return loc.checkLeader()
}
// checkLeader returns this location boxed as a Leader. It checks that the
// location is currently a leader and panics if it is not. Note that
// aliasing a leader can make it lose its leadership status. So the
// static type can only be regarded as correct for immediate use.
func (l *Location) checkLeader() *Location {
if l.parent != nil {
panic(fmt.Errorf("Location is not leader. Loc=%p Parent=%p Leader=%p",
l, l.parent, l.leader()))
}
return l
}
// isEmpty returns true if the location contains no interesting information.
// Some location exist to represent structure, before it is known whether
// there will be interesting information at this location.
func (l *Location) isEmpty() bool {
l = l.leader()
if l.nested == nil || l.nested.isNestedEmpty() {
return l.snips.IsEmpty()
}
// nested is not empty
return false
}
// getLeaves traverses a potentionally nested location and collects all
// the snippets in a table associated with the path which leads to them.
func (l *Location) getTable(path snippets.Pathway, table *SnippetTable) {
l = l.leader()
if len(l.snips) != 0 {
table.Add(path, l.snips)
}
if l.nested != nil {
l.nested.getTable(path, table)
}
}
// newEntity creates a new entity nested structure at this location.
func (l *Location) newEntity(t semantic.Type) *entity {
l = l.leader()
e := &entity{self: l, typ: t}
l.nested = e
return e
}
// newContainer creates a new container nested structure at this location.
func (l *Location) newContainer() *container {
l = l.leader()
c := &container{self: l, key: &Location{}, elem: &Location{}}
l.nested = c
return c
}
// newPointer creates a new pointer nested structure at this location.
func (l *Location) newPointer() *pointer {
l = l.leader()
p := &pointer{self: l}
l.nested = p
return p
}
// container returns a nested container structure for this location. A new
// container will be made, if one does not exist. It will panic, if the
// location already represents a nested structure other than a container.
func (l *Location) container(expr semantic.Expression) *container {
l = l.leader()
if l.nested == nil {
return l.newContainer()
}
if c, ok := l.nested.(*container); ok {
return c
} else {
panic(fmt.Errorf("location is not a container %T:%v for %T:%v", l.nested, l.nested, expr, expr))
}
}
// entity returns a nested entity structure for this location.
// A new entity will be made, if one does not exist. It will panic, if
// the location already represents a nested structure other than an entity.
func (l *Location) entity(expr semantic.Expression) *entity {
l = l.leader()
if l.nested == nil {
typ := expr.ExpressionType()
switch typ := typ.(type) {
case *semantic.Reference, *semantic.Class:
return l.newEntity(typ)
default:
panic(fmt.Errorf("Can not make an entity for %T:%v in %T:%v",
typ, typ, expr, expr))
}
}
if e, ok := l.nested.(*entity); ok {
return e
} else {
panic(fmt.Errorf("location is not an entity %T:%v", l.nested, l.nested))
}
}
// underlyingType strips pseudonyms to return the underlying type.
func underlyingType(t semantic.Type) semantic.Type {
if pseudo, ok := t.(*semantic.Pseudonym); ok {
return underlyingType(pseudo.To)
}
return t
}
// pointer returns a nested pointer to container structure for this location.
// A new pointer will be made, if one does not exist. It will panic, if the
// location already represents a nested structure other than an entity.
func (l *Location) pointer(expr semantic.Expression) *pointer {
l = l.leader()
if l.nested == nil {
typ := underlyingType(expr.ExpressionType())
switch typ := typ.(type) {
case *semantic.Pointer:
return l.newPointer()
default:
panic(fmt.Errorf("Can not make a pointer for %T:%v in %T:%v",
typ, typ, expr, expr))
}
}
if p, ok := l.nested.(*pointer); ok {
return p
} else {
panic(fmt.Errorf("location is not a pointer %T:%v", l.nested, l.nested))
}
}
func (l *Location) getNested() Nested {
if l == nil {
return nil
}
l = l.leader()
return l.nested
}
func (l *Location) String() string {
if l.parent != nil {
return fmt.Sprintf("<alias for %s>", l.leader())
}
l.checkLeader()
buf := &bytes.Buffer{}
buf.WriteString("location@")
buf.WriteString(fmt.Sprintf("%p(", l))
for i, a := range l.snips {
if i != 0 {
buf.WriteString(", ")
}
buf.WriteString(fmt.Sprintf("%s", a))
}
buf.WriteString(")")
if l.nested != nil {
buf.WriteString(" is ")
buf.WriteString(fmt.Sprintf("%s", l.nested))
}
return buf.String()
}