/* * 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.model; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import com.oracle.truffle.dsl.processor.ProcessorContext; import com.oracle.truffle.dsl.processor.expression.DSLExpression; import com.oracle.truffle.dsl.processor.java.ElementUtils; public final class SpecializationData extends TemplateMethod { public enum SpecializationKind { UNINITIALIZED, SPECIALIZED, POLYMORPHIC, FALLBACK } private final NodeData node; private SpecializationKind kind; private final List<SpecializationThrowsData> exceptions; private List<GuardExpression> guards = Collections.emptyList(); private List<CacheExpression> caches = Collections.emptyList(); private List<AssumptionExpression> assumptionExpressions = Collections.emptyList(); private List<ShortCircuitData> shortCircuits; private final Set<SpecializationData> replaces = new TreeSet<>(); private final Set<String> replacesNames = new TreeSet<>(); private final Set<SpecializationData> excludedBy = new TreeSet<>(); private String insertBeforeName; private SpecializationData insertBefore; private boolean reachable; private int index; private DSLExpression limitExpression; public SpecializationData(NodeData node, TemplateMethod template, SpecializationKind kind, List<SpecializationThrowsData> exceptions) { super(template); this.node = node; this.kind = kind; this.exceptions = exceptions; this.index = template.getNaturalOrder(); for (SpecializationThrowsData exception : exceptions) { exception.setSpecialization(this); } } public boolean isCacheBoundByGuard(CacheExpression cacheExpression) { VariableElement cachedVariable = cacheExpression.getParameter().getVariableElement(); for (GuardExpression expression : getGuards()) { if (expression.getExpression().findBoundVariableElements().contains(cachedVariable)) { return true; } } for (AssumptionExpression expression : getAssumptionExpressions()) { if (expression.getExpression().findBoundVariableElements().contains(cachedVariable)) { return true; } } // check all next binding caches if they are bound to a guard and use this cache variable boolean found = false; for (CacheExpression expression : getCaches()) { if (cacheExpression == expression) { found = true; } else if (found) { if (expression.getExpression().findBoundVariableElements().contains(cachedVariable)) { if (isCacheBoundByGuard(expression)) { return true; } } } } return false; } public boolean isGuardBoundWithCache(GuardExpression guardExpression) { return !getBoundCaches(guardExpression.getExpression()).isEmpty(); } public Set<CacheExpression> getBoundCaches(DSLExpression guardExpression) { Set<VariableElement> boundVars = guardExpression.findBoundVariableElements(); Set<CacheExpression> foundCaches = new LinkedHashSet<>(); for (CacheExpression cache : getCaches()) { VariableElement cacheVar = cache.getParameter().getVariableElement(); if (boundVars.contains(cacheVar)) { // bound caches for caches are returned before foundCaches.addAll(getBoundCaches(cache.getExpression())); foundCaches.add(cache); } } return foundCaches; } public void setKind(SpecializationKind kind) { this.kind = kind; } public boolean isDynamicParameterBound(DSLExpression expression) { Set<VariableElement> boundVariables = expression.findBoundVariableElements(); for (Parameter parameter : getDynamicParameters()) { if (boundVariables.contains(parameter.getVariableElement())) { return true; } } return false; } public Parameter findByVariable(VariableElement variable) { for (Parameter parameter : getParameters()) { if (ElementUtils.variableEquals(parameter.getVariableElement(), variable)) { return parameter; } } return null; } public DSLExpression getLimitExpression() { return limitExpression; } public void setLimitExpression(DSLExpression limitExpression) { this.limitExpression = limitExpression; } public void setInsertBefore(SpecializationData insertBefore) { this.insertBefore = insertBefore; } public void setInsertBeforeName(String insertBeforeName) { this.insertBeforeName = insertBeforeName; } public SpecializationData getInsertBefore() { return insertBefore; } public String getInsertBeforeName() { return insertBeforeName; } public Set<String> getReplacesNames() { return replacesNames; } public SpecializationData(NodeData node, TemplateMethod template, SpecializationKind kind) { this(node, template, kind, new ArrayList<SpecializationThrowsData>()); } public Set<SpecializationData> getReplaces() { return replaces; } public Set<SpecializationData> getExcludedBy() { return excludedBy; } public void setReachable(boolean reachable) { this.reachable = reachable; } public boolean isReachable() { return reachable; } public boolean isPolymorphic() { return kind == SpecializationKind.POLYMORPHIC; } @Override protected List<MessageContainer> findChildContainers() { List<MessageContainer> sinks = new ArrayList<>(); if (exceptions != null) { sinks.addAll(exceptions); } if (guards != null) { sinks.addAll(guards); } if (caches != null) { sinks.addAll(caches); } if (assumptionExpressions != null) { sinks.addAll(assumptionExpressions); } return sinks; } public boolean hasRewrite(ProcessorContext context) { if (!getExceptions().isEmpty()) { return true; } if (!getGuards().isEmpty()) { return true; } if (!getAssumptionExpressions().isEmpty()) { return true; } for (Parameter parameter : getSignatureParameters()) { NodeChildData child = parameter.getSpecification().getExecution().getChild(); if (child != null) { ExecutableTypeData type = child.findExecutableType(parameter.getType()); if (type == null) { type = child.findAnyGenericExecutableType(context); } if (type.hasUnexpectedValue(context)) { return true; } if (ElementUtils.needsCastTo(type.getReturnType(), parameter.getType())) { return true; } } } return false; } @Override public int compareTo(TemplateMethod other) { if (this == other) { return 0; } else if (!(other instanceof SpecializationData)) { return super.compareTo(other); } SpecializationData m2 = (SpecializationData) other; int kindOrder = kind.compareTo(m2.kind); if (kindOrder != 0) { return kindOrder; } int compare = 0; int order1 = index; int order2 = m2.index; if (order1 != NO_NATURAL_ORDER && order2 != NO_NATURAL_ORDER) { compare = Integer.compare(order1, order2); if (compare != 0) { return compare; } } return super.compareTo(other); } public void setIndex(int order) { this.index = order; } public int getIndex() { return index; } public NodeData getNode() { return node; } public void setGuards(List<GuardExpression> guards) { this.guards = guards; } public boolean isSpecialized() { return kind == SpecializationKind.SPECIALIZED; } public boolean isFallback() { return kind == SpecializationKind.FALLBACK; } public boolean isUninitialized() { return kind == SpecializationKind.UNINITIALIZED; } public List<SpecializationThrowsData> getExceptions() { return exceptions; } public List<GuardExpression> getGuards() { return guards; } public void setShortCircuits(List<ShortCircuitData> shortCircuits) { this.shortCircuits = shortCircuits; } public List<ShortCircuitData> getShortCircuits() { return shortCircuits; } public SpecializationData findNextSpecialization() { List<SpecializationData> specializations = node.getSpecializations(); for (int i = 0; i < specializations.size() - 1; i++) { if (specializations.get(i) == this) { return specializations.get(i + 1); } } return null; } @Override public String toString() { return String.format("%s [id = %s, method = %s, guards = %s, signature = %s]", getClass().getSimpleName(), getId(), getMethod(), getGuards(), getDynamicTypes()); } public boolean isFrameUsed() { return getFrame() != null; } public List<CacheExpression> getCaches() { return caches; } public void setCaches(List<CacheExpression> caches) { this.caches = caches; } public void setAssumptionExpressions(List<AssumptionExpression> assumptionExpressions) { this.assumptionExpressions = assumptionExpressions; } public List<AssumptionExpression> getAssumptionExpressions() { return assumptionExpressions; } public boolean hasMultipleInstances() { if (!getCaches().isEmpty()) { for (GuardExpression guard : getGuards()) { DSLExpression guardExpression = guard.getExpression(); Set<VariableElement> boundVariables = guardExpression.findBoundVariableElements(); if (isDynamicParameterBound(guardExpression)) { for (CacheExpression cache : getCaches()) { if (boundVariables.contains(cache.getParameter().getVariableElement())) { return true; } } } } } return false; } public boolean isConstantLimit() { if (hasMultipleInstances()) { DSLExpression expression = getLimitExpression(); if (expression == null) { return true; } else { Object constant = expression.resolveConstant(); if (constant != null && constant instanceof Integer) { return true; } } return false; } else { return true; } } public int getMaximumNumberOfInstances() { if (hasMultipleInstances()) { DSLExpression expression = getLimitExpression(); if (expression == null) { return 3; // default limit } else { Object constant = expression.resolveConstant(); if (constant != null && constant instanceof Integer) { return (int) constant; } else { return Integer.MAX_VALUE; } } } else { return 1; } } public boolean isReachableAfter(SpecializationData prev) { if (!prev.isSpecialized()) { return true; } if (!prev.getExceptions().isEmpty()) { // may get excluded by exception return true; } if (hasMultipleInstances()) { // may fallthrough due to limit return true; } Iterator<Parameter> currentSignature = getSignatureParameters().iterator(); Iterator<Parameter> prevSignature = prev.getSignatureParameters().iterator(); TypeSystemData typeSystem = prev.getNode().getTypeSystem(); while (currentSignature.hasNext() && prevSignature.hasNext()) { TypeMirror currentType = currentSignature.next().getType(); TypeMirror prevType = prevSignature.next().getType(); if (!typeSystem.isImplicitSubtypeOf(currentType, prevType)) { return true; } } if (!prev.getAssumptionExpressions().isEmpty()) { // TODO: chumer: we could at least check reachability after trivial assumptions // not sure if this is worth it. return true; } Iterator<GuardExpression> prevGuards = prev.getGuards().iterator(); Iterator<GuardExpression> currentGuards = getGuards().iterator(); while (prevGuards.hasNext()) { GuardExpression prevGuard = prevGuards.next(); GuardExpression currentGuard = currentGuards.hasNext() ? currentGuards.next() : null; if (currentGuard == null || !currentGuard.implies(prevGuard)) { return true; } } return false; } public CacheExpression findCache(Parameter resolvedParameter) { for (CacheExpression cache : getCaches()) { if (cache.getParameter() == resolvedParameter) { return cache; } } return null; } }