/*
* Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.dsl.processor.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.lang.model.type.TypeMirror;
import com.oracle.truffle.dsl.processor.java.ElementUtils;
import com.oracle.truffle.dsl.processor.model.GuardExpression;
import com.oracle.truffle.dsl.processor.model.NodeData;
import com.oracle.truffle.dsl.processor.model.SpecializationData;
import com.oracle.truffle.dsl.processor.model.TemplateMethod.TypeSignature;
/**
* Class creates groups of specializations to optimize the layout of generated executeAndSpecialize
* and generic execute methods.
*/
public final class SpecializationGroup {
private final List<TypeGuard> typeGuards;
private final List<GuardExpression> guards;
private final NodeData node;
private final SpecializationData specialization;
private final List<SpecializationGroup> children = new ArrayList<>();
private SpecializationGroup parent;
private SpecializationGroup(SpecializationData data) {
this.node = data.getNode();
this.typeGuards = new ArrayList<>();
this.guards = new ArrayList<>();
this.specialization = data;
TypeSignature sig = data.getTypeSignature();
for (int i = 1; i < sig.size(); i++) {
typeGuards.add(new TypeGuard(sig.get(i), i - 1));
}
this.guards.addAll(data.getGuards());
}
public SpecializationGroup(List<SpecializationGroup> children, List<TypeGuard> typeGuardsMatches, List<GuardExpression> guardMatches) {
assert !children.isEmpty() : "children must not be empty";
this.typeGuards = typeGuardsMatches;
this.guards = guardMatches;
this.node = children.get(0).node;
this.specialization = null;
updateChildren(children);
}
public boolean isEmpty() {
return typeGuards.isEmpty() && guards.isEmpty();
}
public List<TypeGuard> getAllGuards() {
List<TypeGuard> collectedGuards = new ArrayList<>();
collectedGuards.addAll(typeGuards);
if (parent != null) {
collectedGuards.addAll(parent.getAllGuards());
}
return collectedGuards;
}
public List<SpecializationData> collectSpecializations() {
List<SpecializationData> specializations = new ArrayList<>();
if (specialization != null) {
specializations.add(specialization);
}
for (SpecializationGroup group : children) {
specializations.addAll(group.collectSpecializations());
}
return specializations;
}
public List<GuardExpression> findElseConnectableGuards() {
if (!getTypeGuards().isEmpty()) {
return Collections.emptyList();
}
if (getGuards().isEmpty()) {
return Collections.emptyList();
}
List<GuardExpression> elseConnectableGuards = new ArrayList<>();
int guardIndex = 0;
while (guardIndex < getGuards().size() && findNegatedGuardInPrevious(getGuards().get(guardIndex)) != null) {
elseConnectableGuards.add(getGuards().get(guardIndex));
guardIndex++;
}
return elseConnectableGuards;
}
private GuardExpression findNegatedGuardInPrevious(GuardExpression guard) {
SpecializationGroup previous = this.getPreviousGroup();
if (previous == null) {
return null;
}
List<GuardExpression> elseConnectedGuards = previous.findElseConnectableGuards();
if (previous == null || previous.getGuards().size() != elseConnectedGuards.size() + 1) {
return null;
}
/* Guard is else branch can be connected in previous specialization. */
if (elseConnectedGuards.contains(guard)) {
return guard;
}
GuardExpression previousGuard = previous.getGuards().get(elseConnectedGuards.size());
if (guard.equalsNegated(previousGuard)) {
return guard;
}
return null;
}
private void updateChildren(List<SpecializationGroup> childs) {
if (!children.isEmpty()) {
children.clear();
}
this.children.addAll(childs);
for (SpecializationGroup child : childs) {
child.parent = this;
}
}
public SpecializationGroup getParent() {
return parent;
}
public List<TypeGuard> getTypeGuards() {
return typeGuards;
}
public List<GuardExpression> getGuards() {
return guards;
}
public List<SpecializationGroup> getChildren() {
return children;
}
public SpecializationData getSpecialization() {
return specialization;
}
private static SpecializationGroup combine(List<SpecializationGroup> groups) {
if (groups.isEmpty()) {
throw new IllegalArgumentException("empty combinations");
}
if (groups.size() == 1) {
return null;
}
List<TypeGuard> typeGuardsMatches = new ArrayList<>();
List<GuardExpression> guardMatches = new ArrayList<>();
SpecializationGroup first = groups.get(0);
List<SpecializationGroup> others = groups.subList(1, groups.size());
outer: for (TypeGuard typeGuard : first.typeGuards) {
for (SpecializationGroup other : others) {
if (!other.typeGuards.contains(typeGuard)) {
// type guards can be combined unordered
continue outer;
}
}
typeGuardsMatches.add(typeGuard);
}
outer: for (GuardExpression guard : first.guards) {
for (SpecializationGroup other : others) {
if (!other.guards.contains(guard)) {
// we must break here. One guard may depend on the other.
break outer;
}
}
guardMatches.add(guard);
}
// check for guards for required type casts
for (Iterator<GuardExpression> iterator = guardMatches.iterator(); iterator.hasNext();) {
GuardExpression guardMatch = iterator.next();
if (!guardMatch.getExpression().findBoundVariables().isEmpty()) {
iterator.remove();
}
// TODO we need to be smarter here with bound parameters.
}
if (typeGuardsMatches.isEmpty() && guardMatches.isEmpty()) {
return null;
}
for (SpecializationGroup group : groups) {
group.typeGuards.removeAll(typeGuardsMatches);
group.guards.removeAll(guardMatches);
}
List<SpecializationGroup> newChildren = new ArrayList<>(groups);
return new SpecializationGroup(newChildren, typeGuardsMatches, guardMatches);
}
public static SpecializationGroup create(SpecializationData specialization) {
return new SpecializationGroup(specialization);
}
public static SpecializationGroup createDefault(List<SpecializationData> specializations) {
List<SpecializationGroup> groups = new ArrayList<>();
for (SpecializationData specialization : specializations) {
groups.add(new SpecializationGroup(specialization));
}
SpecializationGroup group = new SpecializationGroup(createCombinationalGroups(groups), Collections.<TypeGuard> emptyList(), Collections.<GuardExpression> emptyList());
return group;
}
public static SpecializationGroup createFlat(List<SpecializationData> specializations) {
SpecializationGroup group = createDefault(specializations);
// trim groups
while (group.isEmpty() && group.getChildren().size() == 1) {
group = group.getChildren().iterator().next();
}
return group;
}
@Override
public String toString() {
return "SpecializationGroup [typeGuards=" + typeGuards + ", guards=" + guards + "]";
}
private static List<SpecializationGroup> createCombinationalGroups(List<SpecializationGroup> groups) {
if (groups.size() <= 1) {
return groups;
}
List<SpecializationGroup> newGroups = new ArrayList<>();
int i = 0;
for (i = 0; i < groups.size();) {
SpecializationGroup combined = null;
for (int j = groups.size(); j > i + 1; j--) {
combined = combine(groups.subList(i, j));
if (combined != null) {
break;
}
}
SpecializationGroup newGroup;
if (combined == null) {
newGroup = groups.get(i);
i++;
} else {
newGroup = combined;
List<SpecializationGroup> originalGroups = new ArrayList<>(combined.children);
combined.updateChildren(createCombinationalGroups(originalGroups));
i += originalGroups.size();
}
newGroups.add(newGroup);
}
return newGroups;
}
public SpecializationGroup getPreviousGroup() {
if (parent == null || parent.children.isEmpty()) {
return null;
}
int index = parent.children.indexOf(this);
if (index <= 0) {
return null;
}
return parent.children.get(index - 1);
}
public int getUncheckedSpecializationIndex() {
int groupMaxIndex = getMaxSpecializationIndex();
int genericIndex = node.getSpecializations().indexOf(node.getGenericSpecialization());
if (groupMaxIndex >= genericIndex) {
// no minimum state check for an generic index
groupMaxIndex = -1;
}
if (groupMaxIndex > -1) {
// no minimum state check if already checked by parent group
int parentMaxIndex = -1;
if (getParent() != null) {
parentMaxIndex = getParent().getMaxSpecializationIndex();
}
if (groupMaxIndex == parentMaxIndex) {
groupMaxIndex = -1;
}
}
return groupMaxIndex;
}
public int getMaxSpecializationIndex() {
if (specialization != null) {
return specialization.getNode().getSpecializations().indexOf(specialization);
} else {
int max = Integer.MIN_VALUE;
for (SpecializationGroup childGroup : getChildren()) {
max = Math.max(max, childGroup.getMaxSpecializationIndex());
}
return max;
}
}
public static final class TypeGuard {
private final int signatureIndex;
private final TypeMirror type;
public TypeGuard(TypeMirror type, int signatureIndex) {
this.type = type;
this.signatureIndex = signatureIndex;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + signatureIndex;
result = prime * result + type.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (getClass() != obj.getClass()) {
return false;
}
TypeGuard other = (TypeGuard) obj;
if (signatureIndex != other.signatureIndex) {
return false;
} else if (!ElementUtils.typeEquals(type, other.type)) {
return false;
}
return true;
}
public int getSignatureIndex() {
return signatureIndex;
}
public TypeMirror getType() {
return type;
}
}
public SpecializationGroup getPrevious() {
if (getParent() == null) {
return null;
}
List<SpecializationGroup> parentChildren = getParent().getChildren();
int index = parentChildren.indexOf(this);
if (index <= 0) {
return null;
}
return parentChildren.get(index - 1);
}
public List<SpecializationData> getAllSpecializations() {
SpecializationGroup p = this;
while (p.getParent() != null) {
p = p.getParent();
}
return p.collectSpecializations();
}
public boolean isLast() {
SpecializationGroup p = getParent();
if (p == null) {
return true;
}
if (p.getChildren().indexOf(this) == p.getChildren().size() - 1) {
return p.isLast();
}
return false;
}
public SpecializationGroup getLast() {
if (children.isEmpty()) {
return null;
}
return children.get(children.size() - 1);
}
private boolean hasFallthrough;
public void setFallthrough(boolean hasFallthrough) {
this.hasFallthrough = hasFallthrough;
}
public boolean hasFallthrough() {
if (hasFallthrough) {
return true;
}
SpecializationGroup lastChild = getLast();
if (lastChild != null) {
return lastChild.hasFallthrough();
}
return false;
}
}