package com.oracle.truffle.dsl.processor.generator;
import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.createTransferToInterpreterAndInvalidate;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.boxType;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.executableEquals;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.findAnnotationMirror;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.firstLetterLowerCase;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.firstLetterUpperCase;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.getAnnotationValue;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.getSimpleName;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.getTypeId;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.getVisibility;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isAssignable;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isObject;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isPrimitive;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isSubtype;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isSubtypeBoxed;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isVoid;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.modifiers;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.needsCastTo;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.setVisibility;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.typeEquals;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.uniqueSortedTypes;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import com.oracle.truffle.dsl.processor.ProcessorContext;
import com.oracle.truffle.dsl.processor.TruffleProcessorOptions;
import com.oracle.truffle.dsl.processor.TruffleTypes;
import com.oracle.truffle.dsl.processor.expression.DSLExpression;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.AbstractDSLExpressionVisitor;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Binary;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Call;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.ClassLiteral;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.DSLExpressionReducer;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Negate;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Variable;
import com.oracle.truffle.dsl.processor.java.ElementUtils;
import com.oracle.truffle.dsl.processor.java.model.CodeAnnotationMirror;
import com.oracle.truffle.dsl.processor.java.model.CodeAnnotationValue;
import com.oracle.truffle.dsl.processor.java.model.CodeExecutableElement;
import com.oracle.truffle.dsl.processor.java.model.CodeNames;
import com.oracle.truffle.dsl.processor.java.model.CodeTree;
import com.oracle.truffle.dsl.processor.java.model.CodeTreeBuilder;
import com.oracle.truffle.dsl.processor.java.model.CodeTypeElement;
import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.ArrayCodeTypeMirror;
import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.DeclaredCodeTypeMirror;
import com.oracle.truffle.dsl.processor.java.model.CodeTypeParameterElement;
import com.oracle.truffle.dsl.processor.java.model.CodeVariableElement;
import com.oracle.truffle.dsl.processor.java.model.GeneratedTypeMirror;
import com.oracle.truffle.dsl.processor.library.ExportsGenerator;
import com.oracle.truffle.dsl.processor.model.AssumptionExpression;
import com.oracle.truffle.dsl.processor.model.CacheExpression;
import com.oracle.truffle.dsl.processor.model.CreateCastData;
import com.oracle.truffle.dsl.processor.model.ExecutableTypeData;
import com.oracle.truffle.dsl.processor.model.GuardExpression;
import com.oracle.truffle.dsl.processor.model.ImplicitCastData;
import com.oracle.truffle.dsl.processor.model.NodeChildData;
import com.oracle.truffle.dsl.processor.model.NodeData;
import com.oracle.truffle.dsl.processor.model.NodeExecutionData;
import com.oracle.truffle.dsl.processor.model.NodeFieldData;
import com.oracle.truffle.dsl.processor.model.Parameter;
import com.oracle.truffle.dsl.processor.model.SpecializationData;
import com.oracle.truffle.dsl.processor.model.SpecializationThrowsData;
import com.oracle.truffle.dsl.processor.model.TemplateMethod;
import com.oracle.truffle.dsl.processor.model.TypeSystemData;
import com.oracle.truffle.dsl.processor.parser.SpecializationGroup;
import com.oracle.truffle.dsl.processor.parser.SpecializationGroup.TypeGuard;
public class FlatNodeGenFactory {
public static final int DEFAULT_MAX_BIT_WIDTH = 32;
private static final String FRAME_VALUE = TemplateMethod.FRAME_NAME;
private static final String NAME_SUFFIX = "_";
private static final String VARARGS_NAME = "args";
private final ProcessorContext context;
private final TruffleTypes types = ProcessorContext.getInstance().getTypes();
private final NodeData node;
private final TypeSystemData typeSystem;
private final TypeMirror genericType;
private final Set<TypeMirror> expectedTypes = new HashSet<>();
private List<SpecializationData> reachableSpecializations;
private SpecializationData[] reachableSpecializationsArray;
private final Collection<NodeData> sharingNodes;
private final boolean boxingEliminationEnabled;
private int boxingSplitIndex = 0;
private final MultiStateBitSet multiState;
private final ExcludeBitSet exclude;
private final ExecutableTypeData executeAndSpecializeType;
private boolean fallbackNeedsState = false;
private boolean fallbackNeedsFrame = false;
private final Map<SpecializationData, CodeTypeElement> specializationClasses = new LinkedHashMap<>();
private final Set<SpecializationData> usedInsertAccessorsArray = new LinkedHashSet<>();
private final Set<SpecializationData> = new LinkedHashSet<>();
private final boolean primaryNode;
private final Map<CacheExpression, String> sharedCaches;
private final Map<ExecutableElement, Function<Call, DSLExpression>> substitutions = new LinkedHashMap<>();
private final Map<String, CodeVariableElement> libraryConstants;
private final boolean needsSpecializeLocking;
private final GeneratorMode generatorMode;
public enum GeneratorMode {
DEFAULT,
EXPORTED_MESSAGE
}
public FlatNodeGenFactory(ProcessorContext context, GeneratorMode mode, NodeData node, Map<String, CodeVariableElement> libraryConstants) {
this(context, mode, node, Arrays.asList(node), node.getSharedCaches(), libraryConstants);
}
public FlatNodeGenFactory(ProcessorContext context, GeneratorMode mode, NodeData node,
Collection<NodeData> stateSharingNodes,
Map<CacheExpression, String> sharedCaches,
Map<String, CodeVariableElement> libraryConstants) {
Objects.requireNonNull(node);
this.generatorMode = mode;
this.context = context;
this.sharingNodes = stateSharingNodes;
this.node = node;
this.typeSystem = node.getTypeSystem();
this.genericType = context.getType(Object.class);
this.boxingEliminationEnabled = !TruffleProcessorOptions.generateSlowPathOnly(context.getEnvironment());
this.reachableSpecializations = calculateReachableSpecializations(node);
this.reachableSpecializationsArray = reachableSpecializations.toArray(new SpecializationData[0]);
this.primaryNode = stateSharingNodes.iterator().next() == node;
this.sharedCaches = sharedCaches;
List<Object> stateObjects = new ArrayList<>();
List<SpecializationData> excludeObjects = new ArrayList<>();
int activeStateStartIndex = -1;
int activeStateEndIndex = -1;
boolean volatileState = false;
for (NodeData stateNode : stateSharingNodes) {
boolean primary = stateNode == node;
if (primary && activeStateStartIndex == -1) {
activeStateStartIndex = stateObjects.size();
}
if (!primary && activeStateStartIndex != -1 && activeStateEndIndex == -1) {
activeStateEndIndex = stateObjects.size();
}
boolean needsRewrites = stateNode.needsRewrites(context);
if (!needsRewrites) {
continue;
}
List<SpecializationData> specializations = calculateReachableSpecializations(stateNode);
Set<TypeGuard> implicitCasts = new LinkedHashSet<>();
for (SpecializationData specialization : specializations) {
stateObjects.add(specialization);
int index = 0;
for (Parameter p : specialization.getSignatureParameters()) {
TypeMirror targetType = p.getType();
List<TypeMirror> sourceTypes = stateNode.getTypeSystem().lookupSourceTypes(targetType);
if (sourceTypes.size() > 1) {
implicitCasts.add(new TypeGuard(targetType, index));
}
index++;
}
if (!specialization.getCaches().isEmpty()) {
volatileState = true;
}
for (GuardExpression guard : specialization.getGuards()) {
if (guardNeedsStateBit(specialization, guard)) {
stateObjects.add(guard);
}
}
excludeObjects.add(specialization);
}
stateObjects.addAll(implicitCasts);
}
if (activeStateEndIndex == -1) {
activeStateEndIndex = stateObjects.size();
}
this.multiState = createMultiStateBitset(stateObjects, activeStateStartIndex, activeStateEndIndex, volatileState);
this.exclude = new ExcludeBitSet(excludeObjects.toArray(new SpecializationData[0]), volatileState);
this.executeAndSpecializeType = createExecuteAndSpecializeType();
this.needsSpecializeLocking = exclude.getCapacity() != 0 || reachableSpecializations.stream().anyMatch((s) -> !s.getCaches().isEmpty());
this.libraryConstants = libraryConstants;
this.substitutions.put(ElementUtils.findExecutableElement(types.LibraryFactory, "resolve"),
(binary) -> substituteLibraryCall(binary));
}
private MultiStateBitSet createMultiStateBitset(List<Object> stateObjects, int activeStateStartIndex, int activeStateEndIndex, boolean volatileState) {
int maxBits = TruffleProcessorOptions.stateBitWidth(context.getEnvironment());
int usedBits = 0;
List<StateBitSet> allStateBits = new ArrayList<>();
List<Object> currentElements = new ArrayList<>();
SpecializationData currentSpecialization = null;
Set<SpecializationData> relevantSpecializations = new LinkedHashSet<>();
int activeStartBits = 0;
int activeEndBits = -1;
for (int i = 0; i < stateObjects.size(); i++) {
Object o = stateObjects.get(i);
if (activeEndBits == -1 && activeStateEndIndex == i) {
activeEndBits = allStateBits.size();
}
int currentBits = getRequiredStateBits(typeSystem, o);
if (usedBits + currentBits > maxBits) {
if (usedBits == 0 || currentElements.isEmpty()) {
throw new AssertionError("single object bit size too high for range");
}
allStateBits.add(new StateBitSet(currentElements.toArray(),
relevantSpecializations.toArray(new SpecializationData[0]),
volatileState, allStateBits.size()));
currentElements.clear();
relevantSpecializations.clear();
usedBits = 0;
}
if (activeStartBits == 0 && activeStateStartIndex == i) {
activeStartBits = allStateBits.size();
}
if (o instanceof SpecializationData) {
currentSpecialization = (SpecializationData) o;
}
relevantSpecializations.add(currentSpecialization);
currentElements.add(o);
usedBits += currentBits;
}
if (activeEndBits == -1) {
activeEndBits = allStateBits.size();
}
allStateBits.add(new StateBitSet(currentElements.toArray(),
relevantSpecializations.toArray(new SpecializationData[0]),
volatileState, allStateBits.size()));
List<StateBitSet> activeStateBits = allStateBits.subList(activeStartBits, activeEndBits + 1);
return new MultiStateBitSet(allStateBits, activeStateBits);
}
private boolean needsRewrites() {
return node.needsRewrites(context);
}
private boolean hasMultipleNodes() {
return sharingNodes.size() > 1;
}
private String createSpecializationTypeName(SpecializationData s) {
if (hasMultipleNodes()) {
return firstLetterUpperCase(getNodePrefix(s)) + firstLetterUpperCase(s.getId()) + "Data";
} else {
return firstLetterUpperCase(s.getId()) + "Data";
}
}
private String createSpecializationFieldName(SpecializationData s) {
if (hasMultipleNodes()) {
return firstLetterLowerCase(getNodePrefix(s)) + "_" + firstLetterLowerCase(s.getId()) + "_cache";
} else {
return firstLetterLowerCase(s.getId()) + "_cache";
}
}
private String createFieldName(SpecializationData specialization, Parameter cacheParameter) {
if (useSpecializationClass(specialization)) {
return cacheParameter.getLocalName() + "_";
} else {
String prefix = "";
if (hasMultipleNodes()) {
prefix = firstLetterLowerCase(getNodePrefix(specialization)) + "_";
}
if (reachableSpecializations.size() > 1) {
prefix = prefix + firstLetterLowerCase(specialization.getId()) + "_";
}
return prefix + cacheParameter.getLocalName() + "_";
}
}
private static String getNodePrefix(SpecializationData specialization) {
String name = specialization.getNode().getNodeId();
if (name.endsWith("Node")) {
name = name.substring(0, name.length() - 4);
}
return name;
}
private String createAssumptionFieldName(SpecializationData specialization, AssumptionExpression assumption) {
if (useSpecializationClass(specialization)) {
return assumption.getId() + "_";
} else {
return firstLetterLowerCase(specialization.getId()) + "_" + assumption.getId() + "_";
}
}
private static String createSpecializationLocalName(SpecializationData s) {
if (s == null) {
return null;
}
return "s" + s.getIndex() + "_";
}
private static String nodeFieldName(NodeExecutionData execution) {
if (execution.getChild() == null || execution.getChild().needsGeneratedField()) {
return execution.getName() + NAME_SUFFIX;
} else {
return execution.getName();
}
}
private static String accessNodeField(NodeExecutionData execution) {
if (execution.getChild() == null || execution.getChild().needsGeneratedField()) {
return "this." + nodeFieldName(execution);
} else {
String access = "super." + execution.getChild().getName();
if (execution.hasChildArrayIndex()) {
access += "[" + execution.getChildArrayIndex() + "]";
}
return access;
}
}
private boolean useSpecializationClass(SpecializationData specialization) {
for (CacheExpression expression : specialization.getCaches()) {
if (expression.getDefaultExpression() == null) {
continue;
}
if (sharedCaches.containsKey(expression)) {
return false;
}
if (isNodeInterfaceArray(expression.getDefaultExpression().getResolvedType())) {
return true;
}
}
int size = 0;
for (CacheExpression expression : specialization.getCaches()) {
if (expression.isAlwaysInitialized()) {
continue;
}
TypeMirror type = expression.getParameter().getType();
if (isPrimitive(type)) {
switch (type.getKind()) {
case BOOLEAN:
case BYTE:
size++;
break;
case CHAR:
case SHORT:
size += 2;
break;
case INT:
case FLOAT:
size += 4;
break;
case LONG:
case DOUBLE:
size += 8;
break;
}
} else {
size += 4;
}
}
if (size > 8 && !hasMultipleNodes()) {
return true;
}
return specialization.getMaximumNumberOfInstances() > 1;
}
private static boolean needsFrameToExecute(List<SpecializationData> specializations) {
for (SpecializationData specialization : specializations) {
if (specialization.getFrame() != null) {
return true;
}
}
return false;
}
private static String createImplicitTypeStateLocalName(Parameter execution) {
String name = firstLetterLowerCase(getTypeId(execution.getType()));
return name + "Cast" + execution.getSpecification().getExecution().getIndex();
}
private static boolean mayBeExcluded(SpecializationData specialization) {
if (specialization.isPolymorphic()) {
return false;
} else if (specialization.isUninitialized()) {
return false;
}
return !specialization.getExceptions().isEmpty() || !specialization.getExcludedBy().isEmpty();
}
public CodeTypeElement create(CodeTypeElement clazz) {
if (primaryNode) {
for (NodeChildData child : node.getChildren()) {
clazz.addOptional(createAccessChildMethod(child, false));
}
for (NodeFieldData field : node.getFields()) {
if (!field.isGenerated()) {
continue;
}
Set<Modifier> fieldModifiers;
if (field.isSettable()) {
fieldModifiers = modifiers(PRIVATE);
} else {
fieldModifiers = modifiers(PRIVATE, FINAL);
}
clazz.add(new CodeVariableElement(fieldModifiers, field.getType(), field.getName()));
if (field.getGetter() != null && field.getGetter().getModifiers().contains(Modifier.ABSTRACT)) {
CodeExecutableElement method = CodeExecutableElement.clone(field.getGetter());
method.getModifiers().remove(Modifier.ABSTRACT);
method.createBuilder().startReturn().string("this.").string(field.getName()).end();
clazz.add(method);
}
if (field.isSettable()) {
CodeExecutableElement method = CodeExecutableElement.clone(field.getSetter());
method.renameArguments(field.getName());
method.getModifiers().remove(Modifier.ABSTRACT);
method.createBuilder().startStatement().string("this.").string(field.getName()).string(" = ", field.getName()).end();
clazz.add(method);
}
}
for (ExecutableElement superConstructor : GeneratorUtils.findUserConstructors(node.getTemplateType().asType())) {
clazz.add(createNodeConstructor(clazz, superConstructor));
}
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getChild() != null && execution.getChild().needsGeneratedField()) {
clazz.add(createNodeField(PRIVATE, execution.getNodeType(), nodeFieldName(execution),
types.Node_Child));
}
}
}
createFields(clazz);
TypeMirror genericReturnType = node.getPolymorphicSpecialization().getReturnType().getType();
List<ExecutableTypeData> executableTypes = filterExecutableTypes(node.getExecutableTypes(),
reachableSpecializations);
List<ExecutableTypeData> genericExecutableTypes = new ArrayList<>();
List<ExecutableTypeData> specializedExecutableTypes = new ArrayList<>();
List<ExecutableTypeData> voidExecutableTypes = new ArrayList<>();
for (ExecutableTypeData type : executableTypes) {
if (isVoid(type.getReturnType())) {
voidExecutableTypes.add(type);
} else if (type.hasUnexpectedValue() && !typeEquals(genericReturnType, type.getReturnType())) {
specializedExecutableTypes.add(type);
} else {
genericExecutableTypes.add(type);
}
}
if (genericExecutableTypes.size() > 1) {
boolean hasGenericTypeMatch = false;
for (ExecutableTypeData genericExecutable : genericExecutableTypes) {
if (typeEquals(genericExecutable.getReturnType(), genericReturnType)) {
hasGenericTypeMatch = true;
break;
}
}
if (hasGenericTypeMatch) {
for (ListIterator<ExecutableTypeData> iterator = genericExecutableTypes.listIterator(); iterator.hasNext();) {
ExecutableTypeData executableTypeData = iterator.next();
if (!isAssignable(genericReturnType, executableTypeData.getReturnType())) {
iterator.remove();
specializedExecutableTypes.add(executableTypeData);
}
}
}
}
SpecializationData fallback = node.getGenericSpecialization();
if (fallback.getMethod() != null && fallback.isReachable()) {
clazz.add(createFallbackGuard());
}
for (ExecutableTypeData type : genericExecutableTypes) {
createExecute(clazz, type, Collections.<ExecutableTypeData> emptyList());
}
for (ExecutableTypeData type : specializedExecutableTypes) {
createExecute(clazz, type, genericExecutableTypes);
}
for (ExecutableTypeData type : voidExecutableTypes) {
List<ExecutableTypeData> genericAndSpecialized = new ArrayList<>();
genericAndSpecialized.addAll(genericExecutableTypes);
genericAndSpecialized.addAll(specializedExecutableTypes);
createExecute(clazz, type, genericAndSpecialized);
}
clazz.addOptional(createExecuteAndSpecialize());
final ReportPolymorphismAction reportPolymorphismAction = reportPolymorphismAction(node, reachableSpecializations);
if (reportPolymorphismAction.required()) {
clazz.addOptional(createCheckForPolymorphicSpecialize(reportPolymorphismAction));
if (requiresCacheCheck(reportPolymorphismAction)) {
clazz.addOptional(createCountCaches());
}
}
AnnotationMirror nodeInfo = null;
try {
nodeInfo = ElementUtils.findAnnotationMirror(node.getTemplateType(), types.NodeInfo);
} catch (UnsupportedOperationException e) {
}
String cost = nodeInfo != null ? ElementUtils.getAnnotationValue(VariableElement.class, nodeInfo, "cost").getSimpleName().toString() : null;
if (cost == null || cost.equals("MONOMORPHIC") ) {
if (primaryNode) {
clazz.add(createGetCostMethod(false));
}
}
for (TypeMirror type : uniqueSortedTypes(expectedTypes, false)) {
if (!typeSystem.hasType(type)) {
clazz.addOptional(TypeSystemCodeGenerator.createExpectMethod(PRIVATE, typeSystem,
context.getType(Object.class), type));
}
}
clazz.getEnclosedElements().addAll(removeThisMethods.values());
for (SpecializationData specialization : specializationClasses.keySet()) {
CodeTypeElement type = specializationClasses.get(specialization);
if (getInsertAccessorSet(true).contains(specialization)) {
type.add(createInsertAccessor(true));
}
if (getInsertAccessorSet(false).contains(specialization)) {
type.add(createInsertAccessor(false));
}
}
if (isGenerateIntrospection()) {
generateIntrospectionInfo(clazz);
}
if (node.isUncachable() && node.isGenerateUncached()) {
CodeTypeElement uncached = GeneratorUtils.createClass(node, null, modifiers(PRIVATE, STATIC, FINAL), "Uncached", node.getTemplateType().asType());
uncached.getEnclosedElements().addAll(createUncachedFields());
for (NodeFieldData field : node.getFields()) {
if (!field.isGenerated()) {
continue;
}
if (field.getGetter() != null && field.getGetter().getModifiers().contains(Modifier.ABSTRACT)) {
CodeExecutableElement method = CodeExecutableElement.clone(field.getGetter());
method.getModifiers().remove(Modifier.ABSTRACT);
method.createBuilder().startThrow().startNew(context.getType(UnsupportedOperationException.class)).end().end();
uncached.add(method);
}
if (field.isSettable()) {
CodeExecutableElement method = CodeExecutableElement.clone(field.getSetter());
method.getModifiers().remove(Modifier.ABSTRACT);
method.createBuilder().startThrow().startNew(context.getType(UnsupportedOperationException.class)).end().end();
uncached.add(method);
}
}
generateStatisticsFields(uncached);
for (NodeChildData child : node.getChildren()) {
uncached.addOptional(createAccessChildMethod(child, true));
}
for (ExecutableTypeData type : genericExecutableTypes) {
uncached.add(createUncachedExecute(type));
}
for (ExecutableTypeData type : specializedExecutableTypes) {
uncached.add(createUncachedExecute(type));
}
for (ExecutableTypeData type : voidExecutableTypes) {
uncached.add(createUncachedExecute(type));
}
if (cost == null || cost.equals("MONOMORPHIC") ) {
uncached.add(createGetCostMethod(true));
}
CodeExecutableElement isAdoptable = CodeExecutableElement.cloneNoAnnotations(ElementUtils.findExecutableElement(types.Node, "isAdoptable"));
isAdoptable.createBuilder().returnFalse();
uncached.add(isAdoptable);
clazz.add(uncached);
GeneratedTypeMirror uncachedType = new GeneratedTypeMirror("", uncached.getSimpleName().toString());
CodeVariableElement uncachedField = clazz.add(new CodeVariableElement(modifiers(PRIVATE, STATIC, FINAL), uncachedType, "UNCACHED"));
uncachedField.createInitBuilder().startNew(uncachedType).end();
}
return clazz;
}
public List<CodeVariableElement> createUncachedFields() {
List<CodeVariableElement> fields = new ArrayList<>();
List<CacheExpression> cacheExpressions = computeUniqueReferenceCaches(true);
for (CacheExpression cache : cacheExpressions) {
CodeVariableElement supplierField = new CodeVariableElement(modifiers(PRIVATE, FINAL),
cache.getReferenceType(), createElementReferenceName(cache));
CodeTreeBuilder builder = supplierField.createInitBuilder();
if (cache.isCachedContext()) {
builder.startCall("lookupContextReference").typeLiteral(cache.getLanguageType()).end();
} else {
builder.startCall("lookupLanguageReference").typeLiteral(cache.getLanguageType()).end();
}
fields.add(supplierField);
}
return fields;
}
public CodeTree createInitializeCaches(SpecializationData specialization, List<CacheExpression> expressions,
CodeExecutableElement method, String receiverName) {
CodeTreeBuilder b = CodeTreeBuilder.createBuilder();
FrameState frameState = FrameState.load(this, NodeExecutionMode.SLOW_PATH, method);
NodeExecutionData execution = specialization.getNode().getChildExecutions().get(0);
frameState.set(execution, frameState.getValue(execution).accessWith(CodeTreeBuilder.singleString(receiverName)));
for (CacheExpression cache : expressions) {
Collection<IfTriple> triples = persistAndInitializeCache(frameState, specialization, cache, false, true);
IfTriple.materialize(b, triples, true);
}
return b.build();
}
private static final class ReportPolymorphismAction {
final boolean polymorphism;
final boolean megamorphism;
ReportPolymorphismAction(boolean polymorphism, boolean megamorphism) {
this.polymorphism = polymorphism;
this.megamorphism = megamorphism;
}
public boolean required() {
return polymorphism || megamorphism;
}
}
private static ReportPolymorphismAction reportPolymorphismAction(NodeData node, List<SpecializationData> reachableSpecializations) {
if (reachableSpecializations.size() == 1 && reachableSpecializations.get(0).getMaximumNumberOfInstances() == 1) {
return new ReportPolymorphismAction(false, false);
}
final boolean reportMegamorphism = reachableSpecializations.stream().anyMatch(SpecializationData::isReportMegamorphism);
if (reachableSpecializations.stream().noneMatch(SpecializationData::isReportPolymorphism)) {
return new ReportPolymorphismAction(false, reportMegamorphism);
}
return new ReportPolymorphismAction(node.isReportPolymorphism(), reportMegamorphism);
}
private void generateIntrospectionInfo(CodeTypeElement clazz) {
clazz.getImplements().add(types.Introspection_Provider);
CodeExecutableElement reflection = new CodeExecutableElement(modifiers(PUBLIC), types.Introspection, "getIntrospectionData");
CodeTreeBuilder builder = reflection.createBuilder();
List<SpecializationData> filteredSpecializations = new ArrayList<>();
for (SpecializationData s : node.getSpecializations()) {
if (s.getMethod() == null) {
continue;
}
filteredSpecializations.add(s);
}
ArrayCodeTypeMirror objectArray = new ArrayCodeTypeMirror(context.getType(Object.class));
builder.declaration(objectArray, "data", builder.create().startNewArray(objectArray, CodeTreeBuilder.singleString(String.valueOf(filteredSpecializations.size() + 1))).end().build());
builder.declaration(objectArray, "s", (CodeTree) null);
builder.statement("data[0] = 0");
boolean needsRewrites = needsRewrites();
FrameState frameState = FrameState.load(this, NodeExecutionMode.SLOW_PATH, reflection);
if (needsRewrites) {
builder.tree(multiState.createLoad(frameState));
if (requiresExclude()) {
builder.tree(exclude.createLoad(frameState));
}
}
int index = 1;
for (SpecializationData specialization : filteredSpecializations) {
builder.startStatement().string("s = ").startNewArray(objectArray, CodeTreeBuilder.singleString("3")).end().end();
builder.startStatement().string("s[0] = ").doubleQuote(specialization.getMethodName()).end();
if (needsRewrites) {
builder.startIf().tree(multiState.createContains(frameState, new Object[]{specialization})).end().startBlock();
}
builder.startStatement().string("s[1] = (byte)0b01 /* active */").end();
TypeMirror listType = new DeclaredCodeTypeMirror((TypeElement) context.getDeclaredType(ArrayList.class).asElement(), Arrays.asList(context.getType(Object.class)));
if (!specialization.getCaches().isEmpty()) {
builder.declaration(listType, "cached", "new ArrayList<>()");
boolean useSpecializationClass = useSpecializationClass(specialization);
String name = createSpecializationLocalName(specialization);
if (useSpecializationClass) {
builder.tree(loadSpecializationClass(frameState, specialization));
if (specialization.hasMultipleInstances()) {
builder.startWhile();
} else {
builder.startIf();
}
builder.string(name, " != null");
builder.end();
builder.startBlock();
}
builder.startStatement().startCall("cached", "add");
builder.startStaticCall(context.getType(Arrays.class), "asList");
for (CacheExpression cache : specialization.getCaches()) {
if (cache.isAlwaysInitialized()) {
continue;
}
builder.startGroup();
if (cache.isAlwaysInitialized() && cache.isCachedLibrary()) {
builder.staticReference(createLibraryConstant(libraryConstants, cache.getParameter().getType()));
builder.startCall(".getUncached").end();
} else {
builder.tree(createCacheReference(frameState, specialization, cache));
}
builder.end();
}
builder.end();
builder.end().end();
if (useSpecializationClass) {
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.startStatement().string(name, " = ", name, ".next_").end();
}
builder.end();
}
builder.statement("s[2] = cached");
}
if (needsRewrites) {
builder.end();
if (mayBeExcluded(specialization)) {
builder.startElseIf().tree(exclude.createContains(frameState, new Object[]{specialization})).end().startBlock();
builder.startStatement().string("s[1] = (byte)0b10 /* excluded */").end();
builder.end();
}
builder.startElseBlock();
builder.startStatement().string("s[1] = (byte)0b00 /* inactive */").end();
builder.end();
}
builder.startStatement().string("data[", String.valueOf(index), "] = s").end();
index++;
}
builder.startReturn().startStaticCall(types.Introspection_Provider, "create").string("data").end().end();
clazz.add(reflection);
}
private void createFields(CodeTypeElement clazz) {
if (primaryNode) {
if (multiState.getAllCapacity() > 0) {
multiState.declareFields(clazz);
}
if (exclude.getCapacity() > 0) {
exclude.declareFields(clazz);
}
}
if (primaryNode && !sharedCaches.isEmpty()) {
Set<String> expressions = new HashSet<>();
for (Entry<CacheExpression, String> entry : sharedCaches.entrySet()) {
CacheExpression cache = entry.getKey();
String fieldName = entry.getValue();
if (expressions.contains(fieldName)) {
continue;
}
if (cache.isAlwaysInitialized()) {
continue;
}
expressions.add(fieldName);
Parameter parameter = cache.getParameter();
TypeMirror type = parameter.getType();
Modifier visibility = Modifier.PRIVATE;
CodeVariableElement cachedField;
if (isAssignable(type, types.NodeInterface) && cache.isAdopt()) {
cachedField = createNodeField(visibility, type, fieldName, types.Node_Child);
} else if (isNodeInterfaceArray(type) && cache.isAdopt()) {
cachedField = createNodeField(visibility, type, fieldName, types.Node_Children);
} else {
cachedField = createNodeField(visibility, type, fieldName, null);
AnnotationMirror mirror = findAnnotationMirror(parameter.getVariableElement().getAnnotationMirrors(), types.Cached);
int dimensions = mirror == null ? 0 : getAnnotationValue(Integer.class, mirror, "dimensions");
setFieldCompilationFinal(cachedField, dimensions);
}
clazz.getEnclosedElements().add(cachedField);
}
}
if (primaryNode) {
List<CacheExpression> cacheExpressions = computeUniqueReferenceCaches(false);
for (CacheExpression cache : cacheExpressions) {
CodeVariableElement supplierField = new CodeVariableElement(modifiers(PRIVATE),
cache.getReferenceType(), createElementReferenceName(cache));
supplierField.getAnnotationMirrors().add(new CodeAnnotationMirror(types.CompilerDirectives_CompilationFinal));
clazz.getEnclosedElements().add(supplierField);
}
}
for (SpecializationData specialization : reachableSpecializations) {
List<CodeVariableElement> fields = new ArrayList<>();
boolean useSpecializationClass = useSpecializationClass(specialization);
for (CacheExpression cache : specialization.getCaches()) {
if (cache.isAlwaysInitialized()) {
continue;
}
String sharedName = sharedCaches.get(cache);
if (sharedName != null) {
continue;
}
Parameter parameter = cache.getParameter();
String fieldName = createFieldName(specialization, parameter);
TypeMirror type = parameter.getType();
Modifier visibility = useSpecializationClass ? null : Modifier.PRIVATE;
CodeVariableElement cachedField;
if (isAssignable(type, types.NodeInterface) && cache.isAdopt()) {
cachedField = createNodeField(visibility, type, fieldName, types.Node_Child);
} else if (isNodeInterfaceArray(type) && cache.isAdopt()) {
cachedField = createNodeField(visibility, type, fieldName, types.Node_Children);
} else {
cachedField = createNodeField(visibility, type, fieldName, null);
if (cache.isCached()) {
AnnotationMirror mirror = cache.getMessageAnnotation();
int dimensions = getAnnotationValue(Integer.class, mirror, "dimensions");
setFieldCompilationFinal(cachedField, dimensions);
}
}
fields.add(cachedField);
}
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
String fieldName = createAssumptionFieldName(specialization, assumption);
TypeMirror type;
int compilationFinalDimensions;
if (assumption.getExpression().getResolvedType().getKind() == TypeKind.ARRAY) {
type = new ArrayCodeTypeMirror(types.Assumption);
compilationFinalDimensions = 1;
} else {
type = types.Assumption;
compilationFinalDimensions = -1;
}
CodeVariableElement assumptionField;
if (useSpecializationClass) {
assumptionField = createNodeField(null, type, fieldName, null);
} else {
assumptionField = createNodeField(PRIVATE, type, fieldName, null);
}
setFieldCompilationFinal(assumptionField, compilationFinalDimensions);
fields.add(assumptionField);
}
if (useSpecializationClass) {
TypeMirror baseType;
boolean useNode = specializationClassIsNode(specialization);
if (useNode) {
baseType = types.Node;
} else {
baseType = context.getType(Object.class);
}
String typeName = createSpecializationTypeName(specialization);
CodeTypeElement cacheType = GeneratorUtils.createClass(node, null, modifiers(PRIVATE, FINAL,
STATIC), createSpecializationTypeName(specialization), baseType);
TypeMirror referenceType = new GeneratedTypeMirror("", typeName);
DeclaredType annotationType;
if (useNode) {
annotationType = types.Node_Child;
if (specialization.getMaximumNumberOfInstances() > 1) {
cacheType.add(createNodeField(null, referenceType, "next_", types.Node_Child));
}
CodeExecutableElement getNodeCost = new CodeExecutableElement(modifiers(PUBLIC),
types.NodeCost, "getCost");
getNodeCost.createBuilder().startReturn().staticReference(types.NodeCost,
"NONE").end();
cacheType.add(getNodeCost);
} else {
annotationType = types.CompilerDirectives_CompilationFinal;
if (specialization.getMaximumNumberOfInstances() > 1) {
cacheType.add(createNodeField(null, referenceType, "next_", annotationType));
}
}
cacheType.add(GeneratorUtils.createConstructorUsingFields(modifiers(), cacheType));
cacheType.getEnclosedElements().addAll(fields);
clazz.add(createNodeField(PRIVATE, referenceType,
createSpecializationFieldName(specialization), annotationType));
clazz.add(cacheType);
specializationClasses.put(specialization, cacheType);
} else {
clazz.getEnclosedElements().addAll(fields);
}
}
generateStatisticsFields(clazz);
}
private void generateStatisticsFields(CodeTypeElement clazz) {
if (isGenerateStatistics()) {
CodeTreeBuilder b;
ArrayType stringArray = new ArrayCodeTypeMirror(context.getType(String.class));
b = clazz.add(new CodeVariableElement(modifiers(PRIVATE, STATIC, FINAL), stringArray, "SPECIALIZATION_NAMES")).createInitBuilder();
b.startNewArray(stringArray, null);
for (SpecializationData specialization : reachableSpecializations) {
if (specialization.getMethod() == null) {
continue;
}
b.doubleQuote(specialization.getMethodName());
}
b.end();
b = clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), types.SpecializationStatistics_NodeStatistics, "statistics_")).createInitBuilder();
b.startStaticCall(types.SpecializationStatistics_NodeStatistics, "create").string("this").string("SPECIALIZATION_NAMES").end();
}
}
private boolean isGenerateStatistics() {
return generatorMode == GeneratorMode.DEFAULT && primaryNode && node.isGenerateStatistics();
}
private boolean isGenerateIntrospection() {
return generatorMode == GeneratorMode.DEFAULT && primaryNode && node.isGenerateIntrospection();
}
private List<CacheExpression> computeUniqueReferenceCaches(boolean uncached) {
List<CacheExpression> cacheExpressions = new ArrayList<>();
Set<String> computedContextReferences = new HashSet<>();
Set<String> computedLanguageReferences = new HashSet<>();
for (NodeData sharedNode : this.sharingNodes) {
Collection<SpecializationData> specializations;
if (uncached) {
specializations = sharedNode.computeUncachedSpecializations(calculateReachableSpecializations(sharedNode));
} else {
specializations = calculateReachableSpecializations(sharedNode);
}
for (SpecializationData specialization : specializations) {
for (CacheExpression cache : specialization.getCaches()) {
if (!cache.isCachedContext() && !cache.isCachedLanguage()) {
continue;
}
TypeMirror languageType = cache.getLanguageType();
String qualifiedLanguageTypeName = ElementUtils.getQualifiedName(languageType);
if (cache.isCachedLanguage()) {
if (computedLanguageReferences.contains(qualifiedLanguageTypeName)) {
continue;
} else {
computedLanguageReferences.add(qualifiedLanguageTypeName);
}
}
if (cache.isCachedContext()) {
if (computedContextReferences.contains(qualifiedLanguageTypeName)) {
continue;
} else {
computedContextReferences.add(qualifiedLanguageTypeName);
}
}
cacheExpressions.add(cache);
}
}
}
return cacheExpressions;
}
private static final String INSERT_ACCESSOR_NAME = "insertAccessor";
private CodeExecutableElement createInsertAccessor(boolean array) {
CodeTypeParameterElement tVar = new CodeTypeParameterElement(CodeNames.of("T"), types.Node);
TypeMirror type = tVar.createMirror(null, null);
if (array) {
type = new ArrayCodeTypeMirror(type);
}
CodeExecutableElement insertAccessor = new CodeExecutableElement(modifiers(FINAL), type, INSERT_ACCESSOR_NAME);
insertAccessor.getParameters().add(new CodeVariableElement(type, "node"));
insertAccessor.getTypeParameters().add(tVar);
insertAccessor.createBuilder().startReturn().string("super.insert(node)").end();
return insertAccessor;
}
private String useInsertAccessor(SpecializationData specialization, boolean array) {
getInsertAccessorSet(array).add(specialization);
return INSERT_ACCESSOR_NAME;
}
private Set<SpecializationData> getInsertAccessorSet(boolean array) {
if (array) {
return usedInsertAccessorsArray;
} else {
return usedInsertAccessorsSimple;
}
}
private boolean isNodeInterfaceArray(TypeMirror type) {
if (type == null) {
return false;
}
return type.getKind() == TypeKind.ARRAY && isAssignable(((ArrayType) type).getComponentType(), types.NodeInterface);
}
private static void setFieldCompilationFinal(CodeVariableElement field, int dimensions) {
if (field.getModifiers().contains(Modifier.FINAL) && dimensions <= 0) {
return;
}
CodeAnnotationMirror annotation = new CodeAnnotationMirror(ProcessorContext.getInstance().getTypes().CompilerDirectives_CompilationFinal);
if (dimensions > 0 || field.getType().getKind() == TypeKind.ARRAY) {
annotation.setElementValue(annotation.findExecutableElement("dimensions"), new CodeAnnotationValue(dimensions < 0 ? 0 : dimensions));
}
field.getAnnotationMirrors().add(annotation);
}
private boolean specializationClassIsNode(SpecializationData specialization) {
boolean useSpecializationClass = useSpecializationClass(specialization);
if (useSpecializationClass) {
for (CacheExpression cache : specialization.getCaches()) {
TypeMirror type = cache.getParameter().getType();
if (isAssignable(type, types.NodeInterface)) {
return true;
} else if (isNodeInterfaceArray(type)) {
return true;
}
}
}
return false;
}
private boolean requiresExclude() {
for (SpecializationData specialization : reachableSpecializations) {
if (mayBeExcluded(specialization)) {
return true;
}
}
return false;
}
private Element createFallbackGuard() {
boolean frameUsed = false;
List<SpecializationData> specializations = new ArrayList<>(reachableSpecializations);
for (ListIterator<SpecializationData> iterator = specializations.listIterator(); iterator.hasNext();) {
SpecializationData specialization = iterator.next();
if (specialization.isFallback()) {
iterator.remove();
} else if (!specialization.isReachesFallback()) {
iterator.remove();
} else {
if (specialization.isFrameUsedByGuard()) {
frameUsed = true;
}
}
}
SpecializationGroup group = SpecializationGroup.create(specializations);
ExecutableTypeData executableType = node.findAnyGenericExecutableType(context, -1);
CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE), getType(boolean.class), createFallbackName());
FrameState frameState = FrameState.load(this, NodeExecutionMode.FALLBACK_GUARD, method);
if (!frameUsed) {
frameState.removeValue(FRAME_VALUE);
}
fallbackNeedsState = false;
fallbackNeedsFrame = frameUsed;
multiState.createLoad(frameState);
multiState.addParametersTo(frameState, method);
frameState.addParametersTo(method, Integer.MAX_VALUE, FRAME_VALUE);
Set<TypeMirror> thrownTypes = new LinkedHashSet<>();
for (SpecializationData specialization : specializations) {
for (GuardExpression expression : specialization.getGuards()) {
for (ExecutableElement boundMethod : expression.getExpression().findBoundExecutableElements()) {
thrownTypes.addAll(boundMethod.getThrownTypes());
}
}
}
method.getThrownTypes().addAll(thrownTypes);
CodeTree result = visitSpecializationGroup(CodeTreeBuilder.createBuilder(), null, group, executableType, frameState, null);
if (!fallbackNeedsState) {
multiState.removeParametersFrom(method);
}
final CodeTreeBuilder builder = method.createBuilder();
for (SpecializationData implemented : specializations) {
if (implemented.getMaximumNumberOfInstances() > 1) {
method.getAnnotationMirrors().add(createExplodeLoop());
break;
}
}
builder.tree(result);
builder.returnTrue();
if (!accessesCachedState(specializations)) {
method.getModifiers().add(STATIC);
}
return method;
}
private DSLExpression substituteLibraryCall(Call call) {
ClassLiteral literal = (ClassLiteral) call.getParameters().get(0);
CodeVariableElement var = createLibraryConstant(libraryConstants, literal.getLiteral());
String constantName = var.getSimpleName().toString();
Variable singleton = new Variable(null, constantName);
singleton.setResolvedTargetType(var.asType());
singleton.setResolvedVariable(var);
return singleton;
}
public static CodeVariableElement createLibraryConstant(Map<String, CodeVariableElement> constants, TypeMirror libraryTypeMirror) {
TypeElement libraryType = ElementUtils.castTypeElement(libraryTypeMirror);
String name = libraryType.getSimpleName().toString();
String constantName = ElementUtils.createConstantName(name);
CodeVariableElement var;
do {
String useConstantName = constantName = constantName + "_";
TypeElement resolvedLibrary = (TypeElement) ProcessorContext.getInstance().getTypes().LibraryFactory.asElement();
DeclaredCodeTypeMirror constantType = new DeclaredCodeTypeMirror(resolvedLibrary, Arrays.asList(libraryType.asType()));
var = constants.computeIfAbsent(constantName, (c) -> {
CodeVariableElement newVar = new CodeVariableElement(modifiers(PRIVATE, STATIC, FINAL), constantType, useConstantName);
newVar.createInitBuilder().startStaticCall(resolvedLibrary.asType(), "resolve").typeLiteral(libraryType.asType()).end();
return newVar;
});
} while (!ElementUtils.typeEquals(libraryType.asType(), ((DeclaredType) var.getType()).getTypeArguments().get(0)));
return var;
}
private DSLExpression optimizeExpression(DSLExpression expression) {
return expression.reduce(new DSLExpressionReducer() {
public DSLExpression visitVariable(Variable binary) {
return binary;
}
public DSLExpression visitNegate(Negate negate) {
return negate;
}
public DSLExpression visitCall(Call binary) {
for (ExecutableElement substitution : substitutions.keySet()) {
if (ElementUtils.executableEquals(binary.getResolvedMethod(), substitution)) {
return substitutions.get(substitution).apply(binary);
}
}
return binary;
}
public DSLExpression visitBinary(Binary binary) {
return binary;
}
});
}
private static boolean accessesCachedState(List<SpecializationData> specializations) {
final AtomicBoolean needsState = new AtomicBoolean(false);
for (final SpecializationData specialization : specializations) {
if (!specialization.getAssumptionExpressions().isEmpty()) {
needsState.set(true);
break;
}
for (GuardExpression expression : specialization.getGuards()) {
expression.getExpression().accept(new AbstractDSLExpressionVisitor() {
@Override
public void visitVariable(Variable binary) {
if (!needsState.get() && isVariableAccessMember(binary)) {
needsState.set(true);
}
}
private boolean isVariableAccessMember(Variable variable) {
if (variable.getName().equals("null") && variable.getReceiver() == null) {
return false;
}
Parameter p = specialization.findByVariable(variable.getResolvedVariable());
if (p == null && !variable.getResolvedVariable().getModifiers().contains(STATIC)) {
DSLExpression receiver = variable.getReceiver();
if (receiver instanceof Variable) {
return isVariableAccessMember((Variable) receiver);
} else if (receiver instanceof Call) {
return isMethodAccessMember((Call) receiver);
}
return true;
} else if (p != null && p.getSpecification().isCached()) {
CacheExpression cache = specialization.findCache(p);
if (cache != null && cache.isAlwaysInitialized()) {
return false;
}
return true;
}
return false;
}
private boolean isMethodAccessMember(Call call) {
if (!call.getResolvedMethod().getModifiers().contains(STATIC)) {
DSLExpression receiver = call.getReceiver();
if (receiver instanceof Variable) {
return isVariableAccessMember((Variable) receiver);
} else if (receiver instanceof Call) {
return isMethodAccessMember((Call) receiver);
}
return true;
}
return false;
}
@Override
public void visitCall(Call call) {
if (!needsState.get() && isMethodAccessMember(call)) {
needsState.set(true);
}
}
});
}
}
boolean needsStat = needsState.get();
return needsStat;
}
private CodeAnnotationMirror createExplodeLoop() {
return new CodeAnnotationMirror(types.ExplodeLoop);
}
private List<SpecializationData> filterCompatibleSpecializations(Collection<SpecializationData> specializations, ExecutableTypeData forType) {
List<SpecializationData> filteredSpecializations = new ArrayList<>();
outer: for (SpecializationData specialization : specializations) {
if (specialization.isFallback() && specialization.getMethod() == null) {
continue;
}
List<TypeMirror> signatureParameters = forType.getSignatureParameters();
for (int i = 0; i < signatureParameters.size(); i++) {
TypeMirror evaluatedType = signatureParameters.get(i);
TypeMirror specializedType = specialization.findParameterOrDie(node.getChildExecutions().get(i)).getType();
if (typeSystem.lookupCast(evaluatedType, specializedType) == null && !isSubtypeBoxed(context, specializedType, evaluatedType) &&
!isSubtypeBoxed(context, evaluatedType, specializedType)) {
continue outer;
}
}
TypeMirror returnType = forType.getReturnType();
if (!isVoid(returnType) && !isSubtypeBoxed(context, specialization.getReturnType().getType(), returnType) &&
!isSubtypeBoxed(context, returnType, specialization.getReturnType().getType())) {
continue outer;
}
filteredSpecializations.add(specialization);
}
return filteredSpecializations;
}
private List<SpecializationData> filterImplementedSpecializations(List<SpecializationData> specializations, TypeMirror expectedReturnType) {
List<SpecializationData> filteredSpecializations = new ArrayList<>();
TypeMirror returnType = boxType(context, expectedReturnType);
for (SpecializationData specialization : specializations) {
TypeMirror specializationReturnType = boxType(context, specialization.getReturnType().getType());
if (typeEquals(specializationReturnType, returnType)) {
filteredSpecializations.add(specialization);
}
}
return filteredSpecializations;
}
private List<ExecutableTypeData> filterCompatibleExecutableTypes(ExecutableTypeData type, List<ExecutableTypeData> genericExecutes) {
List<ExecutableTypeData> compatible = new ArrayList<>();
outer: for (ExecutableTypeData genericExecute : genericExecutes) {
if (genericExecute.getEvaluatedCount() != type.getEvaluatedCount()) {
continue;
}
for (int i = 0; i < genericExecute.getEvaluatedCount(); i++) {
TypeMirror sourceType = type.getSignatureParameters().get(i);
TypeMirror targetType = genericExecute.getSignatureParameters().get(i);
if (!isAssignable(sourceType, targetType)) {
continue outer;
}
}
if (!isVoid(type.getReturnType()) && !isSubtypeBoxed(context, type.getReturnType(), genericExecute.getReturnType()) &&
!isSubtypeBoxed(context, genericExecute.getReturnType(), type.getReturnType())) {
continue outer;
}
compatible.add(genericExecute);
}
return compatible;
}
private CodeExecutableElement createExecute(CodeTypeElement clazz, ExecutableTypeData type, List<ExecutableTypeData> delegateableTypes) {
final List<SpecializationData> allSpecializations = reachableSpecializations;
final List<SpecializationData> compatibleSpecializations = filterCompatibleSpecializations(allSpecializations, type);
List<SpecializationData> implementedSpecializations;
if (delegateableTypes.isEmpty()) {
implementedSpecializations = compatibleSpecializations;
} else {
implementedSpecializations = filterImplementedSpecializations(compatibleSpecializations, type.getReturnType());
}
CodeExecutableElement method = createExecuteMethod(type);
FrameState frameState = FrameState.load(this, type, Integer.MAX_VALUE, NodeExecutionMode.FAST_PATH, method);
if (type.getMethod() == null) {
frameState.addParametersTo(method, Integer.MAX_VALUE, FRAME_VALUE);
} else {
renameOriginalParameters(type, method, frameState);
}
clazz.add(method);
CodeTreeBuilder builder = method.createBuilder();
if (compatibleSpecializations.size() != implementedSpecializations.size()) {
ExecuteDelegationResult delegation = createExecuteDelegation(builder, frameState, type, delegateableTypes, compatibleSpecializations, implementedSpecializations);
builder.tree(delegation.tree);
if (!delegation.hasFallthrough) {
return method;
}
}
if (implementedSpecializations.isEmpty()) {
implementedSpecializations = compatibleSpecializations;
}
if (implementedSpecializations.isEmpty()) {
builder.tree(GeneratorUtils.createShouldNotReachHere("Delegation failed."));
} else {
SpecializationGroup group = SpecializationGroup.create(implementedSpecializations);
builder.tree(createFastPath(builder, implementedSpecializations, group, type, frameState));
}
return method;
}
public CodeExecutableElement createUncached() {
SpecializationData fallback = node.getGenericSpecialization();
TypeMirror returnType = fallback.getReturnType().getType();
List<TypeMirror> parameterTypes = new ArrayList<>();
for (Parameter parameter : fallback.getSignatureParameters()) {
parameterTypes.add(parameter.getType());
}
ExecutableTypeData forType = new ExecutableTypeData(node, returnType, "uncached", null, parameterTypes);
return createUncachedExecute(forType);
}
private CodeExecutableElement createUncachedExecute(ExecutableTypeData forType) {
final Collection<SpecializationData> allSpecializations = node.computeUncachedSpecializations(reachableSpecializations);
final List<SpecializationData> compatibleSpecializations = filterCompatibleSpecializations(allSpecializations, forType);
CodeExecutableElement method = createExecuteMethod(forType);
FrameState frameState = FrameState.load(this, forType, Integer.MAX_VALUE, NodeExecutionMode.UNCACHED, method);
if (forType.getMethod() == null) {
frameState.addParametersTo(method, Integer.MAX_VALUE, FRAME_VALUE);
} else {
renameOriginalParameters(forType, method, frameState);
}
boolean isExecutableInUncached = forType.getEvaluatedCount() != node.getExecutionCount() && !node.getChildren().isEmpty();
if (!isExecutableInUncached) {
method.getAnnotationMirrors().add(new CodeAnnotationMirror(types.CompilerDirectives_TruffleBoundary));
}
if (forType.getMethod() != null) {
method.getModifiers().addAll(forType.getMethod().getModifiers());
method.getModifiers().remove(Modifier.ABSTRACT);
}
CodeTreeBuilder builder = method.createBuilder();
if (isExecutableInUncached) {
builder.tree(GeneratorUtils.createShouldNotReachHere("This execute method cannot be used for uncached node versions as it requires child nodes to be present. " +
"Use an execute method that takes all arguments as parameters."));
} else {
SpecializationGroup group = SpecializationGroup.create(compatibleSpecializations);
FrameState originalFrameState = frameState.copy();
builder.tree(visitSpecializationGroup(builder, null, group, forType, frameState, allSpecializations));
if (group.hasFallthrough()) {
builder.tree(createThrowUnsupported(builder, originalFrameState));
}
}
return method;
}
private ExecuteDelegationResult createExecuteDelegation(CodeTreeBuilder parent, FrameState frameState, ExecutableTypeData type,
List<ExecutableTypeData> delegateableTypes, final List<SpecializationData> compatibleSpecializations, List<SpecializationData> implementedSpecializations) {
CodeTreeBuilder builder = parent.create();
List<SpecializationData> notImplemented = new ArrayList<>(compatibleSpecializations);
for (SpecializationData specialization : implementedSpecializations) {
notImplemented.remove(specialization);
}
if (notImplemented.isEmpty()) {
throw new AssertionError();
}
List<ExecutableTypeData> compatibleDelegateTypes = filterCompatibleExecutableTypes(type, delegateableTypes);
List<ExecutableTypeData> delegatedDelegateTypes = new ArrayList<>();
CodeTreeBuilder delegateBuilder = builder.create();
boolean elseIf = false;
boolean coversAllSpecializations = false;
if (boxingEliminationEnabled) {
Set<TypeMirror> optimizeTypes = new HashSet<>();
for (SpecializationData specialization : reachableSpecializations) {
TypeMirror returnType = specialization.getReturnType().getType();
if (isPrimitive(returnType)) {
optimizeTypes.add(returnType);
}
}
for (TypeMirror optimizedType : uniqueSortedTypes(optimizeTypes, true)) {
ExecutableTypeData delegateType = null;
for (ExecutableTypeData compatibleType : compatibleDelegateTypes) {
if (typeEquals(compatibleType.getReturnType(), optimizedType)) {
delegateType = compatibleType;
break;
}
}
if (delegateType != null) {
List<SpecializationData> delegateSpecializations = filterImplementedSpecializations(
filterCompatibleSpecializations(reachableSpecializations, delegateType), delegateType.getReturnType());
coversAllSpecializations = delegateSpecializations.size() == reachableSpecializations.size();
if (!coversAllSpecializations) {
builder.tree(multiState.createLoad(frameState, delegateSpecializations));
elseIf = delegateBuilder.startIf(elseIf);
delegateBuilder.startGroup();
CodeTree tree = multiState.createContainsOnly(frameState, 0, -1, delegateSpecializations.toArray(), reachableSpecializationsArray);
if (!tree.isEmpty()) {
delegateBuilder.tree(tree);
delegateBuilder.string(" && ");
}
delegateBuilder.tree(multiState.createIsNotAny(frameState, reachableSpecializationsArray));
delegateBuilder.end();
delegateBuilder.end();
delegateBuilder.startBlock();
}
delegatedDelegateTypes.add(delegateType);
delegateBuilder.tree(createCallExecute(type, delegateType, frameState));
if (!coversAllSpecializations) {
delegateBuilder.end();
}
if (coversAllSpecializations) {
break;
}
}
}
}
if (!compatibleDelegateTypes.isEmpty() && !coversAllSpecializations) {
ExecutableTypeData delegateType = compatibleDelegateTypes.get(0);
coversAllSpecializations = notImplemented.size() == reachableSpecializations.size();
if (!coversAllSpecializations) {
builder.tree(multiState.createLoad(frameState, notImplemented));
elseIf = delegateBuilder.startIf(elseIf);
delegateBuilder.tree(multiState.createContains(frameState, notImplemented.toArray())).end();
delegateBuilder.startBlock();
}
delegatedDelegateTypes.add(delegateType);
delegateBuilder.tree(createCallExecute(type, delegateType, frameState));
if (!coversAllSpecializations) {
delegateBuilder.end();
}
}
boolean hasUnexpected = false;
for (ExecutableTypeData delegateType : delegatedDelegateTypes) {
if (needsUnexpectedResultException(delegateType)) {
hasUnexpected = true;
break;
}
}
if (hasUnexpected) {
builder.startTryBlock();
builder.tree(delegateBuilder.build());
builder.end().startCatchBlock(types.UnexpectedResultException, "ex");
if (isVoid(type.getReturnType())) {
builder.returnStatement();
} else {
builder.startReturn();
builder.tree(expectOrCast(getType(Object.class), type, CodeTreeBuilder.singleString("ex")));
builder.end();
}
builder.end();
} else {
builder.tree(delegateBuilder.build());
}
return new ExecuteDelegationResult(builder.build(), !coversAllSpecializations);
}
private String createFallbackName() {
if (hasMultipleNodes()) {
String messageName = node.getNodeId();
if (messageName.endsWith("Node")) {
messageName = messageName.substring(0, messageName.length() - 4);
}
return firstLetterLowerCase(messageName) + "FallbackGuard_";
} else {
return "fallbackGuard_";
}
}
private String createExecuteAndSpecializeName() {
if (hasMultipleNodes()) {
String messageName = node.getNodeId();
if (messageName.endsWith("Node")) {
messageName = messageName.substring(0, messageName.length() - 4);
}
return firstLetterLowerCase(messageName) + "AndSpecialize";
} else {
return "executeAndSpecialize";
}
}
private CodeExecutableElement createExecuteAndSpecialize() {
if (!needsRewrites()) {
return null;
}
String frame = null;
if (needsFrameToExecute(reachableSpecializations)) {
frame = FRAME_VALUE;
}
TypeMirror returnType = executeAndSpecializeType.getReturnType();
CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE), returnType, createExecuteAndSpecializeName());
final FrameState frameState = FrameState.load(this, NodeExecutionMode.SLOW_PATH, method);
frameState.addParametersTo(method, Integer.MAX_VALUE, frame);
final CodeTreeBuilder builder = method.createBuilder();
if (needsSpecializeLocking) {
builder.declaration(context.getType(Lock.class), "lock", "getLock()");
builder.declaration(context.getType(boolean.class), "hasLock", "true");
builder.statement("lock.lock()");
}
builder.tree(multiState.createLoad(frameState));
if (requiresExclude()) {
builder.tree(exclude.createLoad(frameState));
}
ReportPolymorphismAction reportPolymorphismAction = reportPolymorphismAction(node, reachableSpecializations);
if (reportPolymorphismAction.required()) {
generateSaveOldPolymorphismState(builder, frameState, reportPolymorphismAction);
}
if (needsSpecializeLocking || reportPolymorphismAction.required()) {
builder.startTryBlock();
}
FrameState originalFrameState = frameState.copy();
SpecializationGroup group = createSpecializationGroups();
CodeTree execution = visitSpecializationGroup(builder, null, group, executeAndSpecializeType, frameState, null);
builder.tree(execution);
if (group.hasFallthrough()) {
builder.tree(createThrowUnsupported(builder, originalFrameState));
}
if (needsSpecializeLocking || reportPolymorphismAction.required()) {
builder.end().startFinallyBlock();
if (reportPolymorphismAction.required()) {
generateCheckNewPolymorphismState(builder, reportPolymorphismAction);
}
if (needsSpecializeLocking) {
builder.startIf().string("hasLock").end().startBlock();
builder.statement("lock.unlock()");
builder.end();
}
builder.end();
}
return method;
}
private static final String OLD_PREFIX = "old";
private static final String NEW_PREFIX = "new";
private static final String COUNT_SUFIX = "Count";
private static final String OLD_EXCLUDE = OLD_PREFIX + "Exclude";
private static final String OLD_CACHE_COUNT = OLD_PREFIX + "Cache" + COUNT_SUFIX;
private static final String NEW_EXCLUDE = NEW_PREFIX + "Exclude";
private static final String REPORT_POLYMORPHIC_SPECIALIZE = "reportPolymorphicSpecialize";
private static final String CHECK_FOR_POLYMORPHIC_SPECIALIZE = "checkForPolymorphicSpecialize";
private static final String COUNT_CACHES = "countCaches";
private String createName(String defaultName) {
if (hasMultipleNodes()) {
String messageName = node.getNodeId();
if (messageName.endsWith("Node")) {
messageName = messageName.substring(0, messageName.length() - 4);
}
return firstLetterLowerCase(messageName) + "_" + defaultName;
} else {
return defaultName;
}
}
private boolean requiresCacheCheck(ReportPolymorphismAction reportPolymorphismAction) {
if (!reportPolymorphismAction.polymorphism) {
return false;
}
for (SpecializationData specialization : reachableSpecializations) {
if (useSpecializationClass(specialization) && specialization.getMaximumNumberOfInstances() > 1) {
return true;
}
}
return false;
}
private Element createCheckForPolymorphicSpecialize(ReportPolymorphismAction reportPolymorphismAction) {
final boolean requiresExclude = reportPolymorphismAction.polymorphism && requiresExclude();
final boolean requiresCacheCheck = requiresCacheCheck(reportPolymorphismAction);
TypeMirror returnType = getType(void.class);
CodeExecutableElement executable = new CodeExecutableElement(modifiers(PRIVATE), returnType, createName(CHECK_FOR_POLYMORPHIC_SPECIALIZE));
FrameState frameState = FrameState.load(this, NodeExecutionMode.SLOW_PATH, executable);
final SpecializationData[] maskedElements = reachableSpecializationsReportingMegamorpism();
for (StateBitSet s : multiState.getSets()) {
executable.addParameter(new CodeVariableElement(s.getType(), s.getOldName()));
}
if (requiresExclude) {
executable.addParameter(new CodeVariableElement(exclude.getType(), OLD_EXCLUDE));
}
if (requiresCacheCheck) {
executable.addParameter(new CodeVariableElement(getType(int.class), OLD_CACHE_COUNT));
}
CodeTreeBuilder builder = executable.createBuilder();
if (reportPolymorphismAction.polymorphism) {
for (StateBitSet s : multiState.getSets()) {
builder.declaration(s.getType(), s.getNewName(), s.createMaskedReference(frameState, reachableSpecializationsReportingPolymorphism()));
}
if (requiresExclude) {
builder.declaration(exclude.getType(), NEW_EXCLUDE, exclude.createReference(frameState));
}
}
builder.startIf();
if (reportPolymorphismAction.polymorphism) {
String sep = "";
for (StateBitSet s : multiState.getSets()) {
builder.string(sep);
builder.string("((", s.getOldName(), " ^ ", s.getNewName(), ") != 0)");
sep = " || ";
}
if (requiresExclude) {
builder.string(" || ");
builder.string("(" + OLD_EXCLUDE + " ^ " + NEW_EXCLUDE + ") != 0");
}
if (requiresCacheCheck) {
builder.string(" || " + OLD_CACHE_COUNT + " < " + createName(COUNT_CACHES) + "()");
}
if (reportPolymorphismAction.megamorphism) {
builder.string(" || ");
}
}
if (reportPolymorphismAction.megamorphism) {
String sep = "";
for (StateBitSet s : multiState.getSets()) {
Object[] elements = s.filter(maskedElements);
if (elements.length > 0) {
builder.string(sep);
builder.string("(");
builder.string("(", s.getOldName(), " & ", s.formatMask(s.createMask(elements)));
builder.string(") == 0");
builder.string(" && ");
builder.tree(s.createMaskedReference(frameState, elements));
builder.string(" != 0");
builder.string(")");
sep = " || ";
}
}
}
builder.end();
builder.startBlock().startStatement().startCall("this", REPORT_POLYMORPHIC_SPECIALIZE).end(2);
builder.end();
return executable;
}
private SpecializationData[] reachableSpecializationsReportingPolymorphism() {
return reachableSpecializations.stream().filter(SpecializationData::isReportPolymorphism).toArray(SpecializationData[]::new);
}
private SpecializationData[] reachableSpecializationsReportingMegamorpism() {
return reachableSpecializations.stream().filter(SpecializationData::isReportMegamorphism).toArray(SpecializationData[]::new);
}
private Element createCountCaches() {
TypeMirror returnType = getType(int.class);
CodeExecutableElement executable = new CodeExecutableElement(modifiers(PRIVATE), returnType, createName(COUNT_CACHES));
CodeTreeBuilder builder = executable.createBuilder();
final String cacheCount = "cache" + COUNT_SUFIX;
builder.declaration(context.getType(int.class), cacheCount, "0");
for (SpecializationData specialization : reachableSpecializationsReportingPolymorphism()) {
if (useSpecializationClass(specialization) && specialization.getMaximumNumberOfInstances() > 1) {
String typeName = createSpecializationTypeName(specialization);
String fieldName = createSpecializationFieldName(specialization);
String localName = createSpecializationLocalName(specialization);
builder.declaration(typeName, localName, "this." + fieldName);
builder.startWhile().string(localName, " != null");
builder.end();
builder.startBlock().statement(cacheCount + "++").statement(localName + "= " + localName + ".next_");
builder.end();
}
}
builder.startReturn().statement(cacheCount);
return executable;
}
private void generateCheckNewPolymorphismState(CodeTreeBuilder builder, ReportPolymorphismAction reportPolymorphismAction) {
builder.startIf();
String sep = "";
for (StateBitSet s : multiState.getSets()) {
builder.string(sep);
builder.string(s.getOldName(), " != 0");
sep = " || ";
}
final boolean requiresExclude = reportPolymorphismAction.polymorphism && requiresExclude();
if (requiresExclude) {
builder.string(" || " + OLD_EXCLUDE + " != 0");
}
builder.end();
builder.startBlock();
builder.startStatement();
builder.startCall(createName(CHECK_FOR_POLYMORPHIC_SPECIALIZE));
for (StateBitSet s : multiState.getSets()) {
builder.string(s.getOldName());
}
if (requiresExclude) {
builder.string(OLD_EXCLUDE);
}
if (requiresCacheCheck(reportPolymorphismAction)) {
builder.string(OLD_CACHE_COUNT);
}
builder.end().end().end();
}
private void generateSaveOldPolymorphismState(CodeTreeBuilder builder, FrameState frameState, ReportPolymorphismAction reportPolymorphismAction) {
for (StateBitSet s : multiState.getSets()) {
builder.declaration(s.getType(), s.getOldName(), s.createMaskedReference(frameState, reachableSpecializationsReportingPolymorphism()));
}
if (reportPolymorphismAction.polymorphism && requiresExclude()) {
builder.declaration(exclude.getType(), OLD_EXCLUDE, "exclude");
}
if (requiresCacheCheck(reportPolymorphismAction)) {
builder.declaration(context.getType(int.class), OLD_CACHE_COUNT, createName(COUNT_CACHES) + "()");
}
}
private CodeTree createThrowUnsupported(final CodeTreeBuilder parent, final FrameState frameState) {
CodeTreeBuilder builder = parent.create();
builder.startThrow().startNew(types.UnsupportedSpecializationException);
ExecutableElement method = parent.findMethod();
if (method != null && method.getModifiers().contains(STATIC)) {
builder.string("null");
} else {
builder.string("this");
}
builder.startNewArray(new ArrayCodeTypeMirror(types.Node), null);
List<CodeTree> values = new ArrayList<>();
for (NodeExecutionData execution : node.getChildExecutions()) {
NodeChildData child = execution.getChild();
LocalVariable var = frameState.getValue(execution);
if (child != null && !frameState.getMode().isUncached()) {
builder.string(accessNodeField(execution));
} else {
builder.string("null");
}
if (var != null) {
values.add(var.createReference());
}
}
builder.end();
builder.trees(values.toArray(new CodeTree[0]));
builder.end().end();
return builder.build();
}
private CodeTree createFastPath(CodeTreeBuilder parent, List<SpecializationData> allSpecializations, SpecializationGroup originalGroup, final ExecutableTypeData currentType,
FrameState frameState) {
final CodeTreeBuilder builder = parent.create();
boolean needsRewrites = needsRewrites();
if (needsRewrites) {
builder.tree(multiState.createLoad(frameState, allSpecializations));
}
int sharedExecutes = 0;
for (NodeExecutionData execution : node.getChildExecutions()) {
boolean canExecuteChild = execution.getIndex() < currentType.getEvaluatedCount();
for (TypeGuard checkedGuard : originalGroup.getTypeGuards()) {
if (checkedGuard.getSignatureIndex() == execution.getIndex()) {
canExecuteChild = true;
break;
}
}
if (!canExecuteChild) {
break;
}
for (TypeGuard checkedGuard : originalGroup.getTypeGuards()) {
if (resolveOptimizedImplicitSourceTypes(execution, checkedGuard.getType()).size() > 1) {
canExecuteChild = false;
break;
}
}
if (!canExecuteChild) {
break;
}
builder.tree(createFastPathExecuteChild(builder, frameState.copy(), frameState, currentType, originalGroup, execution));
sharedExecutes++;
}
List<BoxingSplit> boxingSplits = parameterBoxingElimination(originalGroup, sharedExecutes);
if (boxingSplits.isEmpty()) {
builder.tree(executeFastPathGroup(builder, frameState, currentType, originalGroup, sharedExecutes, null));
addExplodeLoop(builder, originalGroup);
} else {
FrameState originalFrameState = frameState.copy();
boolean elseIf = false;
for (BoxingSplit split : boxingSplits) {
elseIf = builder.startIf(elseIf);
builder.startGroup();
List<SpecializationData> specializations = split.group.collectSpecializations();
CodeTree tree = multiState.createContainsOnly(frameState, 0, -1, specializations.toArray(), allSpecializations.toArray());
if (!tree.isEmpty()) {
builder.tree(tree);
builder.string(" && ");
}
builder.tree(multiState.createIsNotAny(frameState, allSpecializations.toArray()));
builder.end();
builder.end().startBlock();
builder.tree(wrapInAMethod(builder, split.group, originalFrameState, split.getName(),
executeFastPathGroup(builder, frameState.copy(), currentType, split.group, sharedExecutes, specializations)));
builder.end();
}
builder.startElseBlock();
builder.tree(wrapInAMethod(builder, originalGroup, originalFrameState, "generic", executeFastPathGroup(builder, frameState, currentType, originalGroup, sharedExecutes, null)));
builder.end();
}
return builder.build();
}
private void addExplodeLoop(final CodeTreeBuilder builder, SpecializationGroup originalGroup) {
for (SpecializationData implemented : originalGroup.collectSpecializations()) {
if (implemented.getMaximumNumberOfInstances() > 1) {
((CodeExecutableElement) builder.findMethod()).getAnnotationMirrors().add(createExplodeLoop());
break;
}
}
}
private CodeTree wrapInAMethod(CodeTreeBuilder parent, SpecializationGroup group, FrameState frameState, String suffix, CodeTree codeTree) {
CodeExecutableElement parentMethod = (CodeExecutableElement) parent.findMethod();
CodeTypeElement parentClass = (CodeTypeElement) parentMethod.getEnclosingElement();
String name = parentMethod.getSimpleName().toString() + "_" + suffix + (boxingSplitIndex++);
CodeExecutableElement method = parentClass.add(new CodeExecutableElement(modifiers(Modifier.PRIVATE), parentMethod.getReturnType(), name));
multiState.addParametersTo(frameState, method);
frameState.addParametersTo(method, Integer.MAX_VALUE, FRAME_VALUE);
CodeTreeBuilder builder = method.createBuilder();
builder.tree(codeTree);
method.getThrownTypes().addAll(parentMethod.getThrownTypes());
addExplodeLoop(builder, group);
CodeTreeBuilder parentBuilder = parent.create();
parentBuilder.startReturn();
parentBuilder.startCall(method.getSimpleName().toString());
multiState.addReferencesTo(frameState, parentBuilder);
frameState.addReferencesTo(parentBuilder, FRAME_VALUE);
parentBuilder.end();
parentBuilder.end();
return parentBuilder.build();
}
private CodeTree executeFastPathGroup(final CodeTreeBuilder parent, FrameState frameState, final ExecutableTypeData currentType, SpecializationGroup group, int sharedExecutes,
List<SpecializationData> allowedSpecializations) {
CodeTreeBuilder builder = parent.create();
if (currentType.getMethod() != null && currentType.getMethod().isVarArgs()) {
int readVarargsCount = node.getSignatureSize() - (currentType.getEvaluatedCount() - 1);
int offset = node.getSignatureSize() - 1;
for (int i = 0; i < readVarargsCount; i++) {
NodeExecutionData execution = node.getChildExecutions().get(offset + i);
LocalVariable var = frameState.getValue(execution);
if (var != null) {
builder.tree(var.createDeclaration(var.createReference()));
frameState.setValue(execution, var.accessWith(null));
}
}
}
FrameState originalFrameState = frameState.copy();
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getIndex() < sharedExecutes) {
continue;
}
builder.tree(createFastPathExecuteChild(builder, originalFrameState, frameState, currentType, group, execution));
}
builder.tree(visitSpecializationGroup(builder, null, group, currentType, frameState, allowedSpecializations));
if (group.hasFallthrough()) {
builder.tree(createTransferToInterpreterAndInvalidate());
builder.tree(createCallExecuteAndSpecialize(currentType, originalFrameState));
}
return builder.build();
}
private List<BoxingSplit> parameterBoxingElimination(SpecializationGroup group, int evaluatedcount) {
if (!boxingEliminationEnabled) {
return Collections.emptyList();
}
List<SpecializationData> allSpecializations = group.collectSpecializations();
List<Set<TypeGuard>> signatures = new ArrayList<>();
List<List<SpecializationData>> signatureSpecializations = new ArrayList<>();
for (SpecializationData specialization : allSpecializations) {
int index = -1;
List<TypeGuard> guards = new ArrayList<>();
for (Parameter p : specialization.getSignatureParameters()) {
index++;
if (!isPrimitive(p.getType())) {
continue;
} else if (index < evaluatedcount) {
continue;
} else {
NodeChildData child = p.getSpecification().getExecution().getChild();
if (child != null && child.findExecutableType(p.getType()) == null) {
continue;
}
}
guards.add(new TypeGuard(p.getType(), index));
}
if (!guards.isEmpty()) {
boolean directFound = false;
for (int i = 0; i < signatures.size(); i++) {
if (guards.containsAll(signatures.get(i))) {
if (signatures.get(i).containsAll(guards)) {
directFound = true;
}
signatureSpecializations.get(i).add(specialization);
}
}
if (!directFound) {
signatures.add(new LinkedHashSet<>(guards));
List<SpecializationData> specializations = new ArrayList<>();
specializations.add(specialization);
signatureSpecializations.add(specializations);
}
}
}
List<BoxingSplit> groups = new ArrayList<>();
for (int i = 0; i < signatureSpecializations.size(); i++) {
List<SpecializationData> groupedSpecialization = signatureSpecializations.get(i);
if (allSpecializations.size() == groupedSpecialization.size()) {
continue;
}
Set<TypeGuard> signature = signatures.get(i);
TypeMirror[] signatureMirrors = new TypeMirror[signature.size()];
int index = 0;
for (TypeGuard typeGuard : signature) {
signatureMirrors[index] = typeGuard.getType();
index++;
}
groups.add(new BoxingSplit(SpecializationGroup.create(groupedSpecialization), signatureMirrors));
}
Collections.sort(groups, new Comparator<BoxingSplit>() {
public int compare(BoxingSplit o1, BoxingSplit o2) {
return Integer.compare(o2.primitiveSignature.length, o1.primitiveSignature.length);
}
});
return groups;
}
private CodeTree createFastPathExecuteChild(final CodeTreeBuilder parent, FrameState originalFrameState, FrameState frameState, final ExecutableTypeData currentType, SpecializationGroup group,
NodeExecutionData execution) {
CodeTreeBuilder builder = parent.create();
LocalVariable var = frameState.getValue(execution);
if (var == null) {
TypeMirror targetType;
TypeGuard eliminatedGuard = null;
if (boxingEliminationEnabled) {
for (TypeGuard checkedGuard : group.getTypeGuards()) {
if (!isPrimitive(checkedGuard.getType())) {
continue;
} else if (node.getChildExecutions().get(checkedGuard.getSignatureIndex()).getChild().findExecutableType(checkedGuard.getType()) == null) {
continue;
}
if (checkedGuard.getSignatureIndex() == execution.getIndex()) {
eliminatedGuard = checkedGuard;
break;
}
}
}
if (eliminatedGuard != null) {
group.getTypeGuards().remove(eliminatedGuard);
targetType = eliminatedGuard.getType();
} else {
targetType = execution.getChild().findAnyGenericExecutableType(context).getReturnType();
}
var = frameState.createValue(execution, targetType).nextName();
LocalVariable fallbackVar;
List<TypeMirror> originalSourceTypes = typeSystem.lookupSourceTypes(targetType);
List<TypeMirror> sourceTypes = resolveOptimizedImplicitSourceTypes(execution, targetType);
if (sourceTypes.size() > 1) {
TypeGuard typeGuard = new TypeGuard(targetType, execution.getIndex());
TypeMirror generic = node.getPolymorphicSpecialization().findParameterOrDie(execution).getType();
fallbackVar = originalFrameState.createValue(execution, generic);
Collections.reverse(sourceTypes);
CodeTree access = var.createReference();
boolean first = true;
for (TypeMirror sType : sourceTypes) {
if (typeEquals(sType, targetType)) {
continue;
}
String localName = createSourceTypeLocalName(var, sType);
builder.declaration(sType, localName, CodeTreeBuilder.createBuilder().defaultValue(sType).build());
CodeTreeBuilder accessBuilder = builder.create();
accessBuilder.startParantheses();
CodeTree containsOnly = multiState.createContainsOnly(frameState, originalSourceTypes.indexOf(sType), 1, new Object[]{typeGuard},
new Object[]{typeGuard});
if (!containsOnly.isEmpty()) {
accessBuilder.tree(containsOnly);
accessBuilder.string(" && ");
}
accessBuilder.tree(multiState.createIsNotAny(frameState, reachableSpecializationsArray));
accessBuilder.string(" ? ");
if (isPrimitive(sType)) {
accessBuilder.string("(").type(generic).string(") ");
}
accessBuilder.string(localName);
accessBuilder.string(" : ");
if (first && isPrimitive(targetType)) {
accessBuilder.string("(").type(generic).string(") ");
}
accessBuilder.tree(access);
accessBuilder.end();
access = accessBuilder.build();
first = false;
}
fallbackVar = fallbackVar.accessWith(access);
} else {
fallbackVar = var;
}
builder.tree(createAssignExecuteChild(originalFrameState, frameState, builder, execution, currentType, var));
frameState.setValue(execution, var);
originalFrameState.setValue(execution, fallbackVar);
}
return builder.build();
}
private CodeTree createAssignExecuteChild(FrameState originalFrameState, FrameState frameState, CodeTreeBuilder parent, NodeExecutionData execution, ExecutableTypeData forType,
LocalVariable targetValue) {
CodeTreeBuilder builder = parent.create();
ChildExecutionResult executeChild = createExecuteChild(builder, originalFrameState, frameState, execution, targetValue);
builder.tree(createTryExecuteChild(targetValue, executeChild.code, true, executeChild.throwsUnexpectedResult));
builder.end();
if (executeChild.throwsUnexpectedResult) {
builder.startCatchBlock(types.UnexpectedResultException, "ex");
FrameState slowPathFrameState = originalFrameState.copy();
slowPathFrameState.setValue(execution, targetValue.makeGeneric(context).accessWith(CodeTreeBuilder.singleString("ex.getResult()")));
ExecutableTypeData delegateType = node.getGenericExecutableType(forType);
boolean found = false;
for (NodeExecutionData otherExecution : node.getChildExecutions()) {
if (found) {
LocalVariable childEvaluatedValue = slowPathFrameState.createValue(otherExecution, genericType);
builder.tree(createAssignExecuteChild(slowPathFrameState.copy(), slowPathFrameState, builder, otherExecution, delegateType, childEvaluatedValue));
slowPathFrameState.setValue(otherExecution, childEvaluatedValue);
} else {
found = execution == otherExecution;
}
}
builder.tree(createCallExecuteAndSpecialize(forType, slowPathFrameState));
builder.end();
}
return builder.build();
}
private static String createSourceTypeLocalName(LocalVariable targetValue, TypeMirror sType) {
return targetValue.getName() + getSimpleName(sType);
}
private ChildExecutionResult createCallSingleChildExecute(NodeExecutionData execution, LocalVariable target, FrameState frameState, ExecutableTypeData executableType) {
CodeTree execute = callChildExecuteMethod(execution, executableType, frameState);
TypeMirror sourceType = executableType.getReturnType();
TypeMirror targetType = target.getTypeMirror();
CodeTree result = expect(sourceType, targetType, execute);
return new ChildExecutionResult(result, executableType.hasUnexpectedValue() || needsCastTo(sourceType, targetType));
}
private ChildExecutionResult createExecuteChild(CodeTreeBuilder parent, FrameState originalFrameState, FrameState frameState, NodeExecutionData execution, LocalVariable target) {
ChildExecutionResult result;
if (!typeSystem.hasImplicitSourceTypes(target.getTypeMirror())) {
ExecutableTypeData targetExecutable = resolveTargetExecutable(execution, target.typeMirror);
final CodeTreeBuilder builder = parent.create();
result = createCallSingleChildExecute(execution, target, frameState, targetExecutable);
builder.string(target.getName()).string(" = ");
builder.tree(result.code);
result.code = builder.build();
} else {
result = createExecuteChildImplicitCast(parent.create(), originalFrameState, frameState, execution, target);
}
return result;
}
private CodeExecutableElement createNodeConstructor(CodeTypeElement clazz, ExecutableElement superConstructor) {
Set<String> ignoreConstructorFields = new HashSet<>();
for (NodeFieldData field : node.getFields()) {
if (field.isSettable()) {
ignoreConstructorFields.add(field.getName());
}
}
CodeExecutableElement constructor = GeneratorUtils.createConstructorUsingFields(modifiers(), clazz, superConstructor, ignoreConstructorFields);
setVisibility(constructor.getModifiers(), getVisibility(superConstructor.getModifiers()));
constructor.setVarArgs(superConstructor.isVarArgs());
List<CodeVariableElement> childParameters = new ArrayList<>();
for (NodeChildData child : node.getChildren()) {
if (child.needsGeneratedField()) {
childParameters.add(new CodeVariableElement(child.getOriginalType(), child.getName()));
}
}
constructor.getParameters().addAll(superConstructor.getParameters().size(), childParameters);
CodeTreeBuilder builder = constructor.appendBuilder();
List<String> childValues = new ArrayList<>(node.getChildren().size());
if (!node.getChildExecutions().isEmpty()) {
for (NodeChildData child : node.getChildren()) {
if (child.needsGeneratedField()) {
String name = child.getName();
if (child.getCardinality().isMany()) {
CreateCastData createCast = node.findCast(child.getName());
if (createCast != null) {
CodeTree nameTree = CodeTreeBuilder.singleString(name);
CodeTreeBuilder callBuilder = builder.create();
callBuilder.string(name).string(" != null ? ");
callBuilder.tree(callMethod(null, null, createCast.getMethod(), nameTree));
callBuilder.string(" : null");
name += "_";
builder.declaration(child.getNodeType(), name, callBuilder.build());
}
}
childValues.add(name);
}
}
}
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getChild() == null || !execution.getChild().needsGeneratedField()) {
continue;
}
CreateCastData createCast = node.findCast(execution.getChild().getName());
builder.startStatement();
builder.string("this.").string(nodeFieldName(execution)).string(" = ");
String name = childValues.get(node.getChildren().indexOf(execution.getChild()));
CodeTreeBuilder accessorBuilder = builder.create();
accessorBuilder.string(name);
if (execution.hasChildArrayIndex()) {
accessorBuilder.string("[").string(String.valueOf(execution.getChildArrayIndex())).string("]");
}
CodeTree accessor = accessorBuilder.build();
if (createCast != null && execution.getChild().getCardinality().isOne()) {
accessor = callMethod(null, null, createCast.getMethod(), accessor);
}
if (execution.hasChildArrayIndex()) {
CodeTreeBuilder nullCheck = builder.create();
nullCheck.string(name).string(" != null && ").string(String.valueOf(execution.getChildArrayIndex())).string(" < ").string(name).string(".length").string(" ? ");
nullCheck.tree(accessor);
nullCheck.string(" : null");
accessor = nullCheck.build();
}
builder.tree(accessor);
builder.end();
}
return constructor;
}
private List<ExecutableTypeData> filterExecutableTypes(List<ExecutableTypeData> executableTypes, List<SpecializationData> specializations) {
Set<TypeMirror> specializedReturnTypes = new HashSet<>();
for (SpecializationData specialization : specializations) {
specializedReturnTypes.add(specialization.getReturnType().getType());
}
List<ExecutableTypeData> filteredTypes = new ArrayList<>();
outer: for (ExecutableTypeData executable : executableTypes) {
if (executable.getMethod() == null) {
continue;
}
if (executable.isAbstract()) {
filteredTypes.add(executable);
continue;
}
if (executable.isFinal()) {
continue;
}
if (!executable.hasUnexpectedValue()) {
filteredTypes.add(executable);
continue;
} else {
TypeMirror returnType = executable.getReturnType();
if (boxingEliminationEnabled && (isVoid(returnType) || isPrimitive(returnType))) {
for (TypeMirror specializedReturnType : specializedReturnTypes) {
if (isSubtypeBoxed(context, specializedReturnType, returnType)) {
filteredTypes.add(executable);
continue outer;
}
}
}
}
}
Collections.sort(filteredTypes);
return filteredTypes;
}
private Element createGetCostMethod(boolean uncached) {
TypeMirror returnType = types.NodeCost;
CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getCost");
executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
CodeTreeBuilder builder = executable.createBuilder();
if (uncached) {
builder.startReturn().staticReference(types.NodeCost, "MEGAMORPHIC").end();
} else {
if (needsRewrites()) {
FrameState frameState = FrameState.load(this, NodeExecutionMode.UNCACHED, executable);
builder.tree(multiState.createLoadContainsSpecialization(frameState));
builder.startIf().tree(multiState.createIs(frameState, new Object[0], reachableSpecializationsArray)).end();
builder.startBlock();
builder.startReturn().staticReference(types.NodeCost, "UNINITIALIZED").end();
builder.end();
if (reachableSpecializations.size() == 1 && !reachableSpecializations.iterator().next().hasMultipleInstances()) {
builder.startElseBlock();
builder.startReturn().staticReference(types.NodeCost, "MONOMORPHIC").end();
builder.end();
} else {
builder.startElseBlock();
if (multiState.getSets().size() == 1) {
builder.startIf();
builder.tree(multiState.getSets().get(0).createIsOneBitOf(frameState, reachableSpecializationsArray));
builder.end().startBlock();
} else {
builder.declaration("int", "counter", "0");
for (BitSet set : multiState.getSets()) {
Object[] filtered = set.filter(reachableSpecializationsArray);
if (filtered.length == 0) {
continue;
}
builder.startStatement();
builder.string("counter += ");
builder.startStaticCall(ElementUtils.findMethod(Integer.class, "bitCount"));
builder.tree(set.createMaskedReference(frameState, filtered));
builder.end();
builder.end();
}
builder.startIf();
builder.string("counter == 1");
builder.end().startBlock();
}
List<CodeTree> additionalChecks = new ArrayList<>();
for (SpecializationData specialization : reachableSpecializations) {
if (useSpecializationClass(specialization) && specialization.getMaximumNumberOfInstances() > 1) {
String typeName = createSpecializationTypeName(specialization);
String fieldName = createSpecializationFieldName(specialization);
String localName = createSpecializationLocalName(specialization);
builder.declaration(typeName, localName, "this." + fieldName);
CodeTree check = builder.create().startParantheses().string(localName, " == null || ",
localName, ".next_ == null").end().build();
additionalChecks.add(check);
}
}
if (!additionalChecks.isEmpty()) {
builder.startIf().tree(combineTrees(" && ", additionalChecks.toArray(new CodeTree[0]))).end().startBlock();
}
builder.startReturn().staticReference(types.NodeCost, "MONOMORPHIC").end();
if (!additionalChecks.isEmpty()) {
builder.end();
}
builder.end();
builder.end();
builder.startReturn().staticReference(types.NodeCost, "POLYMORPHIC").end();
}
} else {
builder.startReturn().staticReference(types.NodeCost, "MONOMORPHIC").end();
}
}
return executable;
}
private ExecutableElement createAccessChildMethod(NodeChildData child, boolean uncached) {
if (child.getAccessElement() != null && child.getAccessElement().getModifiers().contains(Modifier.ABSTRACT)) {
ExecutableElement getter = (ExecutableElement) child.getAccessElement();
CodeExecutableElement method = CodeExecutableElement.clone(getter);
method.getModifiers().remove(Modifier.ABSTRACT);
List<NodeExecutionData> executions = new ArrayList<>();
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getChild() == child) {
executions.add(execution);
}
}
CodeTreeBuilder builder = method.createBuilder();
if (uncached) {
method.getAnnotationMirrors().add(new CodeAnnotationMirror(types.CompilerDirectives_TruffleBoundary));
builder.tree(GeneratorUtils.createShouldNotReachHere("This getter method cannot be used for uncached node versions as it requires child nodes to be present."));
} else {
if (child.getCardinality().isMany()) {
builder.startReturn().startNewArray((ArrayType) child.getOriginalType(), null);
for (NodeExecutionData execution : executions) {
builder.string(accessNodeField(execution));
}
builder.end().end();
} else {
for (NodeExecutionData execution : executions) {
builder.startReturn().string(accessNodeField(execution)).end();
break;
}
}
}
return method;
}
return null;
}
private static List<SpecializationData> calculateReachableSpecializations(NodeData node) {
List<SpecializationData> specializations = new ArrayList<>();
for (SpecializationData specialization : node.getSpecializations()) {
if (specialization.isReachable() &&
(specialization.isSpecialized()
|| (specialization.isFallback() && specialization.getMethod() != null))) {
specializations.add(specialization);
}
}
return specializations;
}
private TypeMirror getType(Class<?> clazz) {
return context.getType(clazz);
}
static CodeVariableElement createNodeField(Modifier visibility, TypeMirror type, String name, DeclaredType annotationClass, Modifier... modifiers) {
CodeVariableElement childField = new CodeVariableElement(modifiers(modifiers), type, name);
if (annotationClass != null) {
if (annotationClass == ProcessorContext.getInstance().getTypes().CompilerDirectives_CompilationFinal) {
setFieldCompilationFinal(childField, 0);
} else {
childField.getAnnotationMirrors().add(new CodeAnnotationMirror(annotationClass));
}
}
setVisibility(childField.getModifiers(), visibility);
return childField;
}
private static CodeTree callMethod(FrameState frameState, CodeTree receiver, ExecutableElement method, CodeTree... boundValues) {
if (frameState != null) {
frameState.addThrownExceptions(method);
}
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
List<? extends VariableElement> parameters = method.getParameters();
CodeTree useReceiver = receiver;
boolean staticMethod = method.getModifiers().contains(STATIC);
int boundValueIndex = -1;
if (!method.getParameters().isEmpty() && staticMethod) {
VariableElement receiverVar = method.getParameters().get(0);
if (receiverVar.getSimpleName().toString().equals("this")) {
useReceiver = boundValues[0];
parameters = parameters.subList(1, parameters.size());
boundValueIndex = 0;
staticMethod = false;
}
}
if (staticMethod) {
builder.startStaticCall(method);
} else {
builder.startCall(useReceiver, method.getSimpleName().toString());
}
for (VariableElement parameter : parameters) {
boundValueIndex++;
if (boundValueIndex < boundValues.length) {
CodeTree tree = boundValues[boundValueIndex];
if (tree != null) {
builder.tree(tree);
continue;
}
}
builder.defaultValue(parameter.asType());
}
builder.end();
return builder.build();
}
private CodeTree[] bindExecuteMethodParameters(NodeExecutionData execution, ExecutableTypeData method, FrameState frameState) {
List<NodeExecutionData> executeWith = execution != null ? execution.getChild().getExecuteWith() : null;
List<CodeTree> values = new ArrayList<>();
if (method.getFrameParameter() != null) {
LocalVariable frameLocal = frameState.get(FRAME_VALUE);
if (frameLocal == null) {
throw new AssertionError(method.getName() + " requires a frame parameter.");
}
values.add(createParameterReference(frameLocal, method.getMethod(), 0));
}
int evaluatedIndex = 0;
for (int executionIndex = 0; executionIndex < node.getExecutionCount(); executionIndex++) {
NodeExecutionData parameterExecution;
if (executeWith != null && executionIndex < executeWith.size()) {
parameterExecution = executeWith.get(executionIndex);
} else {
parameterExecution = node.getChildExecutions().get(executionIndex);
}
if (evaluatedIndex < method.getEvaluatedCount()) {
TypeMirror targetType = method.getEvaluatedParameters().get(evaluatedIndex);
LocalVariable value = frameState.getValue(parameterExecution);
if (value != null) {
int parameterIndex = method.getParameterIndex(evaluatedIndex);
values.add(createParameterReference(value, method.getMethod(), parameterIndex));
} else {
values.add(CodeTreeBuilder.createBuilder().defaultValue(targetType).build());
}
evaluatedIndex++;
}
}
return values.toArray(new CodeTree[values.size()]);
}
private CodeTree callChildExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, FrameState frameState) {
return callMethod(frameState, CodeTreeBuilder.singleString(accessNodeField(execution)), method.getMethod(), bindExecuteMethodParameters(execution, method, frameState));
}
private CodeTree createParameterReference(LocalVariable sourceVariable, ExecutableElement targetMethod, int targetIndex) {
CodeTree valueReference = sourceVariable.createReference();
TypeMirror sourceType = sourceVariable.getTypeMirror();
VariableElement targetParameter = targetMethod.getParameters().get(targetIndex);
TypeMirror targetType = targetParameter.asType();
if (targetType == null || sourceType == null) {
return valueReference;
}
boolean hasCast = false;
if (needsCastTo(sourceType, targetType)) {
CodeTree castValue = TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference);
hasCast = valueReference != castValue;
valueReference = castValue;
}
if (!typeEquals(sourceType, targetType) && !hasCast) {
Element element = targetMethod.getEnclosingElement();
boolean needsOverloadCast = false;
if (element != null) {
for (ExecutableElement executable : ElementFilter.methodsIn(element.getEnclosedElements())) {
if (executableEquals(executable, targetMethod)) {
continue;
}
if (!executable.getSimpleName().toString().equals(targetMethod.getSimpleName().toString())) {
continue;
}
if (executable.getParameters().size() != targetMethod.getParameters().size()) {
continue;
}
TypeMirror overloadedTarget = executable.getParameters().get(targetIndex).asType();
if (!needsCastTo(sourceType, overloadedTarget)) {
needsOverloadCast = true;
break;
}
}
}
if (needsOverloadCast) {
valueReference = TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference);
}
}
return valueReference;
}
private SpecializationGroup createSpecializationGroups() {
return SpecializationGroup.create(reachableSpecializations);
}
private CodeTree expectOrCast(TypeMirror sourceType, ExecutableTypeData targetType, CodeTree content) {
if (needsUnexpectedResultException(targetType)) {
return expect(sourceType, targetType.getReturnType(), content);
} else {
return cast(sourceType, targetType.getReturnType(), content);
}
}
private CodeTree cast(TypeMirror sourceType, TypeMirror targetType, CodeTree content) {
if (needsCastTo(sourceType, targetType) && !isVoid(sourceType)) {
return TypeSystemCodeGenerator.cast(typeSystem, targetType, content);
} else {
return content;
}
}
private CodeTree expect(TypeMirror sourceType, TypeMirror forType, CodeTree tree) {
if (sourceType == null || needsCastTo(sourceType, forType)) {
expectedTypes.add(forType);
return TypeSystemCodeGenerator.expect(typeSystem, forType, tree);
}
return tree;
}
private CodeExecutableElement createExecuteMethod(ExecutableTypeData executedType) {
TypeMirror returnType = executedType.getReturnType();
String methodName;
if (executedType.getMethod() != null) {
methodName = executedType.getMethod().getSimpleName().toString();
} else {
methodName = executedType.getUniqueName();
}
CodeExecutableElement executable;
if (executedType.getMethod() != null) {
executable = CodeExecutableElement.clone(executedType.getMethod());
executable.getAnnotationMirrors().clear();
executable.getModifiers().remove(ABSTRACT);
for (VariableElement var : executable.getParameters()) {
((CodeVariableElement) var).getAnnotationMirrors().clear();
}
executable.renameArguments(FRAME_VALUE);
if (executable.isVarArgs()) {
((CodeVariableElement) executable.getParameters().get(executable.getParameters().size() - 1)).setName(VARARGS_NAME);
}
} else {
executable = new CodeExecutableElement(modifiers(PUBLIC), returnType, methodName);
}
DeclaredType unexpectedResult = types.UnexpectedResultException;
Iterator<TypeMirror> thrownTypes = executable.getThrownTypes().iterator();
while (thrownTypes.hasNext()) {
if (typeEquals(unexpectedResult, thrownTypes.next())) {
thrownTypes.remove();
}
}
if (needsUnexpectedResultException(executedType)) {
executable.getThrownTypes().add(unexpectedResult);
}
return executable;
}
private void renameOriginalParameters(ExecutableTypeData executedType, CodeExecutableElement executable, FrameState frameState) {
int evaluatedIndex = 0;
for (int executionIndex = 0; executionIndex < node.getExecutionCount(); executionIndex++) {
NodeExecutionData execution = node.getChildExecutions().get(executionIndex);
if (evaluatedIndex < executedType.getEvaluatedCount()) {
TypeMirror evaluatedType = executedType.getEvaluatedParameters().get(evaluatedIndex);
LocalVariable value = frameState.getValue(execution);
if (value != null) {
frameState.setValue(execution, renameExecutableTypeParameter(executable, executedType, evaluatedIndex, evaluatedType, value));
}
evaluatedIndex++;
}
}
}
private static LocalVariable renameExecutableTypeParameter(CodeExecutableElement method, ExecutableTypeData executedType, int evaluatedIndex, TypeMirror targetType, LocalVariable var) {
int parameterIndex = executedType.getParameterIndex(evaluatedIndex);
int varArgsIndex = executedType.getVarArgsIndex(parameterIndex);
LocalVariable returnVar = var;
if (varArgsIndex >= 0) {
returnVar = returnVar.accessWith(CodeTreeBuilder.singleString(VARARGS_NAME + "[" + varArgsIndex + "]"));
} else {
((CodeVariableElement) method.getParameters().get(parameterIndex)).setName(returnVar.getName());
}
if (!isObject(targetType)) {
returnVar = returnVar.newType(targetType);
}
return returnVar;
}
private boolean needsUnexpectedResultException(ExecutableTypeData executedType) {
if (!executedType.hasUnexpectedValue()) {
return false;
}
if (isSubtypeBoxed(context, executeAndSpecializeType.getReturnType(), executedType.getReturnType())) {
return false;
} else {
return true;
}
}
private CodeTree createFastPathExecute(CodeTreeBuilder parent, final ExecutableTypeData forType, SpecializationData specialization, FrameState frameState) {
CodeTreeBuilder builder = parent.create();
int ifCount = 0;
if (specialization.isFallback()) {
builder.startIf().startCall(createFallbackName());
if (fallbackNeedsState) {
multiState.addReferencesTo(frameState, builder);
}
if (fallbackNeedsFrame) {
if (frameState.get(FRAME_VALUE) != null) {
builder.string(FRAME_VALUE);
} else {
builder.nullLiteral();
}
}
frameState.addReferencesTo(builder);
builder.end();
builder.end();
builder.startBlock();
ifCount++;
}
builder.tree(createCallSpecialization(builder, frameState, forType, specialization));
builder.end(ifCount);
return builder.build();
}
private CodeTree createCallSpecialization(CodeTreeBuilder parent, FrameState parentState, final ExecutableTypeData forType, SpecializationData specialization) {
CodeTreeBuilder builder = parent.create();
FrameState frameState = parentState.copy();
for (SpecializationThrowsData throwsData : specialization.getExceptions()) {
frameState.addCaughtException(throwsData.getJavaClass());
}
if (needsSpecializeLocking && frameState.getMode().isSlowPath()) {
builder.statement("lock.unlock()");
builder.statement("hasLock = false");
}
if (specialization.getMethod() == null) {
builder.tree(createThrowUnsupported(builder, frameState));
} else {
CodeTree[] bindings = new CodeTree[specialization.getParameters().size()];
TypeMirror[] bindingTypes = new TypeMirror[specialization.getParameters().size()];
for (int i = 0; i < bindings.length; i++) {
Parameter parameter = specialization.getParameters().get(i);
if (parameter.getSpecification().isCached()) {
LocalVariable var = frameState.get(createFieldName(specialization, parameter));
if (var != null) {
bindings[i] = var.createReference();
} else {
bindings[i] = createCacheReference(frameState, specialization, specialization.findCache(parameter));
}
bindingTypes[i] = parameter.getType();
} else {
LocalVariable variable = bindExpressionVariable(frameState, specialization, parameter);
if (variable != null) {
bindings[i] = createParameterReference(variable, specialization.getMethod(), i);
bindingTypes[i] = variable.getTypeMirror();
} else {
bindingTypes[i] = parameter.getType();
}
}
}
if (isGenerateStatistics()) {
CodeTreeBuilder statistics = builder.create();
statistics.startStatement();
statistics.startCall("statistics_", "acceptExecute");
statistics.string(String.valueOf(specialization.getIntrospectionIndex()));
for (int i = 0; i < bindings.length; i++) {
Parameter parameter = specialization.getParameters().get(i);
if (parameter.getSpecification().isSignature()) {
TypeMirror type = bindingTypes[i];
if (ElementUtils.isFinal(type)) {
statistics.typeLiteral(type);
} else {
statistics.startCall("statistics_", "resolveValueClass");
statistics.tree(bindings[i]);
statistics.end();
}
}
}
statistics.end();
statistics.end();
builder.tree(statistics.build());
}
CodeTree specializationCall = callMethod(frameState, null, specialization.getMethod(), bindings);
if (isVoid(specialization.getMethod().getReturnType())) {
builder.statement(specializationCall);
if (isVoid(forType.getReturnType())) {
builder.returnStatement();
} else {
builder.startReturn().defaultValue(forType.getReturnType()).end();
}
} else {
builder.startReturn();
builder.tree(expectOrCast(specialization.getReturnType().getType(), forType, specializationCall));
builder.end();
}
}
return createCatchRewriteException(builder, specialization, forType, frameState, builder.build());
}
static final class BlockState {
static final BlockState NONE = new BlockState(0, 0);
final int ifCount;
final int blockCount;
private BlockState(int ifCount, int blockCount) {
this.ifCount = ifCount;
this.blockCount = blockCount;
}
BlockState add(BlockState state) {
return new BlockState(ifCount + state.ifCount, blockCount + state.blockCount);
}
BlockState incrementIf() {
return new BlockState(ifCount + 1, blockCount + 1);
}
static BlockState create(int ifCount, int blockCount) {
if (ifCount == 0 && blockCount == 0) {
return NONE;
} else {
return new BlockState(ifCount, blockCount);
}
}
}
private static class IfTriple {
private CodeTree prepare;
private CodeTree condition;
private CodeTree statements;
IfTriple(CodeTree prepare, CodeTree condition, CodeTree statements) {
this.prepare = prepare;
this.condition = condition;
this.statements = statements;
}
private boolean canBeMerged(IfTriple triple) {
boolean prepareSet = !isEmpty(triple.prepare) || !isEmpty(prepare);
boolean conditionSet = !isEmpty(triple.condition) || !isEmpty(condition);
boolean statementsSet = !isEmpty(triple.statements) || !isEmpty(statements);
return conditionSet ^ (prepareSet || statementsSet);
}
private static boolean isEmpty(CodeTree e) {
return e == null || e.isEmpty();
}
@Override
public String toString() {
CodeTreeBuilder b = CodeTreeBuilder.createBuilder();
b.startGroup();
if (!isEmpty(prepare)) {
b.tree(prepare);
}
if (!isEmpty(condition)) {
b.startIf().tree(condition).end().startBlock();
}
if (!isEmpty(statements)) {
b.tree(statements);
}
if (!isEmpty(condition)) {
b.end();
}
b.end();
return b.build().toString();
}
private static IfTriple merge(String conditionSep, Set<IfTriple> triples) {
if (triples.isEmpty()) {
throw new AssertionError();
}
if (triples.size() == 1) {
return triples.iterator().next();
}
CodeTree[] prepareTrees = new CodeTree[triples.size()];
CodeTree[] conditionTrees = new CodeTree[triples.size()];
CodeTree[] statementTrees = new CodeTree[triples.size()];
int index = 0;
for (IfTriple triple : triples) {
prepareTrees[index] = triple.prepare;
conditionTrees[index] = triple.condition;
statementTrees[index] = triple.statements;
index++;
}
return new IfTriple(combineTrees(null, prepareTrees),
combineTrees(conditionSep, conditionTrees),
combineTrees(null, statementTrees));
}
public static List<IfTriple> optimize(List<IfTriple> triples) {
List<IfTriple> newTriples = new ArrayList<>();
Set<IfTriple> mergable = new LinkedHashSet<>();
IfTriple prev = null;
for (IfTriple triple : triples) {
if (prev != null) {
if (prev.canBeMerged(triple)) {
mergable.add(triple);
} else {
newTriples.add(merge(" && ", mergable));
mergable.clear();
}
}
prev = triple;
mergable.add(prev);
}
if (prev != null) {
newTriples.add(merge(" && ", mergable));
}
return newTriples;
}
public static BlockState materialize(CodeTreeBuilder builder, Collection<IfTriple> triples, boolean forceNoBlocks) {
int blockCount = 0;
int ifCount = 0;
boolean otherPrepare = false;
for (IfTriple triple : triples) {
if (triple.prepare != null && !triple.prepare.isEmpty()) {
if (!otherPrepare) {
if (blockCount == 0 && !forceNoBlocks) {
builder.startBlock();
blockCount++;
}
otherPrepare = true;
}
builder.tree(triple.prepare);
}
if (triple.condition != null && !triple.condition.isEmpty()) {
if (forceNoBlocks) {
throw new AssertionError("no blocks forced but block required");
}
builder.startIf().tree(triple.condition).end().startBlock();
blockCount++;
ifCount++;
}
if (triple.statements != null && !triple.statements.isEmpty()) {
builder.tree(triple.statements);
}
}
return BlockState.create(ifCount, blockCount);
}
}
private static boolean guardNeedsStateBit(SpecializationData specialization, GuardExpression guard) {
if (specialization.isReachesFallback() && specialization.isGuardBoundWithCache(guard)) {
return true;
}
return false;
}
private static GuardExpression getGuardThatNeedsStateBit(SpecializationData specialization, GuardExpression guard) {
if (guardNeedsStateBit(specialization, guard)) {
return guard;
}
List<GuardExpression> guards = specialization.getGuards();
int index = guards.indexOf(guard);
if (index < 0) {
throw new AssertionError("guard must be contained");
}
for (int i = index - 1; i >= 0; i--) {
GuardExpression otherGuard = guards.get(i);
if (guardNeedsStateBit(specialization, otherGuard)) {
return otherGuard;
}
}
return null;
}
private CodeTree visitSpecializationGroup(CodeTreeBuilder parent, SpecializationGroup originalPrev, SpecializationGroup group, ExecutableTypeData forType,
FrameState frameState, Collection<SpecializationData> allowedSpecializations) {
final CodeTreeBuilder builder = parent.create();
SpecializationGroup prev = originalPrev;
NodeExecutionMode mode = frameState.getMode();
boolean hasFallthrough = false;
boolean hasImplicitCast = false;
List<IfTriple> cachedTriples = new ArrayList<>();
for (TypeGuard guard : group.getTypeGuards()) {
IfTriple triple = createTypeCheckOrCast(frameState, group, guard, mode, false, true);
if (triple != null) {
cachedTriples.add(triple);
}
hasImplicitCast = hasImplicitCast || node.getTypeSystem().hasImplicitSourceTypes(guard.getType());
if (!mode.isGuardFallback()) {
triple = createTypeCheckOrCast(frameState, group, guard, mode, true, true);
if (triple != null) {
cachedTriples.add(triple);
}
}
}
SpecializationData specialization = group.getSpecialization();
SpecializationData[] specializations = group.collectSpecializations().toArray(new SpecializationData[0]);
List<GuardExpression> guardExpressions = new ArrayList<>(group.getGuards());
if (specialization != null && specialization.hasMultipleInstances()) {
List<GuardExpression> unboundGuards = new ArrayList<>();
for (GuardExpression guard : guardExpressions) {
if (!specialization.isGuardBoundWithCache(guard)) {
unboundGuards.add(guard);
} else {
break;
}
}
cachedTriples.addAll(createMethodGuardChecks(frameState, group, unboundGuards, mode));
guardExpressions.removeAll(unboundGuards);
}
boolean useSpecializationClass = specialization != null && useSpecializationClass(specialization);
boolean needsRewrites = needsRewrites();
if (mode.isFastPath()) {
BlockState ifCount = BlockState.NONE;
final boolean stateGuaranteed = group.isLast() && allowedSpecializations != null && allowedSpecializations.size() == 1 &&
group.getAllSpecializations().size() == allowedSpecializations.size();
if (needsRewrites && (!group.isEmpty() || specialization != null)) {
CodeTree stateCheck = multiState.createContains(frameState, specializations);
if (stateCheck.isEmpty()) {
System.out.println();
}
CodeTree stateGuard = null;
CodeTree assertCheck = null;
if (stateGuaranteed) {
assertCheck = CodeTreeBuilder.createBuilder().startAssert().tree(stateCheck).end().build();
} else {
stateGuard = stateCheck;
}
cachedTriples.add(0, new IfTriple(null, stateGuard, assertCheck));
}
ifCount = ifCount.add(IfTriple.materialize(builder, IfTriple.optimize(cachedTriples), false));
cachedTriples = new ArrayList<>();
String specializationLocalName = null;
if (useSpecializationClass) {
specializationLocalName = createSpecializationLocalName(specialization);
builder.tree(loadSpecializationClass(frameState, specialization));
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.startWhile();
} else {
builder.startIf();
}
builder.string(specializationLocalName, " != null");
builder.end();
builder.startBlock();
ifCount = ifCount.incrementIf();
}
if (specialization != null && !specialization.getAssumptionExpressions().isEmpty()) {
builder.tree(createFastPathAssumptionCheck(builder, specialization, forType, frameState));
}
boolean extractInBoundary = false;
boolean pushEncapsulatingNode = false;
boolean libraryInGuard = false;
if (specialization != null) {
libraryInGuard = specialization.isAnyLibraryBoundInGuard();
pushEncapsulatingNode = specialization.needsPushEncapsulatingNode();
extractInBoundary = specialization.needsTruffleBoundary();
if (extractInBoundary && specialization.needsVirtualFrame()) {
extractInBoundary = false;
}
}
List<IfTriple> nonBoundaryGuards = new ArrayList<>();
guards: for (Iterator<GuardExpression> iterator = guardExpressions.iterator(); iterator.hasNext();) {
GuardExpression guard = iterator.next();
Set<CacheExpression> caches = group.getSpecialization().getBoundCaches(guard.getExpression(), true);
for (CacheExpression cache : caches) {
if (cache.isAlwaysInitialized() && cache.isRequiresBoundary()) {
break guards;
}
}
nonBoundaryGuards.addAll(initializeCaches(frameState, mode, group, caches, true, false));
nonBoundaryGuards.add(createMethodGuardCheck(frameState, group.getSpecialization(), guard, mode));
iterator.remove();
}
if (pushEncapsulatingNode && libraryInGuard) {
GeneratorUtils.pushEncapsulatingNode(builder, "this");
builder.startTryBlock();
}
for (Iterator<GuardExpression> iterator = guardExpressions.iterator(); iterator.hasNext();) {
GuardExpression guard = iterator.next();
Set<CacheExpression> caches = group.getSpecialization().getBoundCaches(guard.getExpression(), true);
nonBoundaryGuards.addAll(initializeCaches(frameState, mode, group, caches, true, false));
nonBoundaryGuards.add(createMethodGuardCheck(frameState, group.getSpecialization(), guard, mode));
}
FrameState innerFrameState = frameState;
BlockState nonBoundaryIfCount = BlockState.NONE;
CodeTreeBuilder innerBuilder;
if (extractInBoundary) {
innerFrameState = frameState.copy();
for (CacheExpression cache : specialization.getCaches()) {
if (cache.isAlwaysInitialized()) {
setCacheInitialized(innerFrameState, specialization, cache, false);
}
}
nonBoundaryIfCount = nonBoundaryIfCount.add(IfTriple.materialize(builder, IfTriple.optimize(nonBoundaryGuards), false));
innerBuilder = extractInBoundaryMethod(builder, frameState, specialization);
for (NodeExecutionData execution : specialization.getNode().getChildExecutions()) {
int index = forType.getVarArgsIndex(forType.getParameterIndex(execution.getIndex()));
if (index != -1) {
LocalVariable var = innerFrameState.getValue(execution);
innerFrameState.set(execution, var.accessWith(CodeTreeBuilder.singleString(var.getName())));
}
}
} else if (pushEncapsulatingNode) {
innerBuilder = builder;
nonBoundaryIfCount = IfTriple.materialize(innerBuilder, IfTriple.optimize(nonBoundaryGuards), false);
} else {
innerBuilder = builder;
cachedTriples.addAll(0, nonBoundaryGuards);
}
if (specialization != null) {
cachedTriples.addAll(initializeCaches(innerFrameState, frameState.getMode(), group, specialization.getCaches(), true, false));
}
if (pushEncapsulatingNode && !libraryInGuard) {
GeneratorUtils.pushEncapsulatingNode(innerBuilder, "this");
innerBuilder.startTryBlock();
}
BlockState innerIfCount = BlockState.NONE;
innerIfCount = innerIfCount.add(IfTriple.materialize(innerBuilder, IfTriple.optimize(cachedTriples), false));
prev = visitSpecializationGroupChildren(builder, innerFrameState, prev, group, forType, allowedSpecializations);
if (specialization != null && (prev == null || prev.hasFallthrough())) {
innerBuilder.tree(createFastPathExecute(builder, forType, specialization, innerFrameState));
}
innerBuilder.end(innerIfCount.blockCount);
if (pushEncapsulatingNode && !libraryInGuard) {
innerBuilder.end().startFinallyBlock();
GeneratorUtils.popEncapsulatingNode(innerBuilder);
innerBuilder.end();
}
hasFallthrough |= innerIfCount.ifCount > 0;
builder.end(nonBoundaryIfCount.blockCount);
if (pushEncapsulatingNode && libraryInGuard) {
builder.end().startFinallyBlock();
GeneratorUtils.popEncapsulatingNode(builder);
builder.end();
}
if (useSpecializationClass && specialization.getMaximumNumberOfInstances() > 1) {
String name = createSpecializationLocalName(specialization);
builder.startStatement().string(name, " = ", name, ".next_").end();
}
builder.end(ifCount.blockCount);
hasFallthrough |= ifCount.ifCount > 0;
} else if (mode.isSlowPath()) {
if (specialization != null && mayBeExcluded(specialization)) {
CodeTree excludeCheck = exclude.createNotContains(frameState, specializations);
cachedTriples.add(0, new IfTriple(null, excludeCheck, null));
}
BlockState outerIfCount = BlockState.NONE;
if (specialization == null) {
cachedTriples.addAll(createMethodGuardChecks(frameState, group, guardExpressions, mode));
outerIfCount = outerIfCount.add(IfTriple.materialize(builder, IfTriple.optimize(cachedTriples), false));
prev = visitSpecializationGroupChildren(builder, frameState, prev, group, forType, allowedSpecializations);
} else {
for (CacheExpression cache : specialization.getCaches()) {
if (!cache.isAlwaysInitialized()) {
continue;
}
CodeTree prepare = CodeTreeBuilder.createBuilder().declarationDefault(cache.getParameter().getType(),
createCacheLocalName(specialization, cache)).build();
cachedTriples.add(0, new IfTriple(prepare, null, null));
}
outerIfCount = outerIfCount.add(IfTriple.materialize(builder, IfTriple.optimize(cachedTriples), false));
String countName = specialization != null ? "count" + specialization.getIndex() + "_" : null;
boolean needsDuplicationCheck = specialization.isGuardBindsCache() || specialization.hasMultipleInstances();
boolean useDuplicateFlag = specialization.isGuardBindsCache() && !specialization.hasMultipleInstances();
String duplicateFoundName = specialization.getId() + "_duplicateFound_";
boolean pushBoundary = specialization.needsPushEncapsulatingNode();
if (pushBoundary) {
builder.startBlock();
GeneratorUtils.pushEncapsulatingNode(builder, "this");
builder.startTryBlock();
}
BlockState innerIfCount = BlockState.NONE;
String specializationLocalName = createSpecializationLocalName(specialization);
if (needsDuplicationCheck) {
builder.tree(createDuplicationCheck(builder, frameState, group, guardExpressions, useDuplicateFlag, countName, duplicateFoundName,
specializationLocalName));
builder.startIf();
if (useDuplicateFlag) {
builder.string("!", duplicateFoundName);
} else {
builder.string(createSpecializationLocalName(specialization), " == null");
}
builder.end().startBlock();
innerIfCount = innerIfCount.incrementIf();
}
FrameState innerFrameState = frameState.copy();
List<IfTriple> innerTripples = new ArrayList<>();
innerTripples.addAll(createMethodGuardChecks(innerFrameState, group, guardExpressions, mode));
List<AssumptionExpression> assumptions = specialization.getAssumptionExpressions();
if (!assumptions.isEmpty()) {
for (AssumptionExpression assumption : assumptions) {
innerTripples.addAll(createAssumptionSlowPathTriples(innerFrameState, group, assumption));
}
}
if (specialization.hasMultipleInstances()) {
DSLExpression limit = optimizeExpression(specialization.getLimitExpression());
Set<CacheExpression> caches = specialization.getBoundCaches(limit, true);
innerTripples.addAll(initializeCaches(innerFrameState, innerFrameState.getMode(), group, caches, true, false));
CodeTree limitExpression = writeExpression(innerFrameState, specialization, limit);
CodeTree limitCondition = CodeTreeBuilder.createBuilder().string(countName).string(" < ").tree(limitExpression).build();
innerTripples.add(new IfTriple(null, limitCondition, null));
assertSpecializationClassNotInitialized(innerFrameState, specialization);
} else if (needsDuplicationCheck) {
innerTripples.add(new IfTriple(null, multiState.createNotContains(innerFrameState, new Object[]{specialization}), null));
}
innerIfCount = innerIfCount.add(IfTriple.materialize(builder, IfTriple.optimize(innerTripples), false));
builder.tree(createSpecialize(builder, innerFrameState, group, specialization));
if (needsDuplicationCheck) {
hasFallthrough = true;
if (useDuplicateFlag) {
builder.startStatement().string(duplicateFoundName, " = true").end();
}
builder.end(innerIfCount.blockCount);
for (CacheExpression cache : specialization.getCaches()) {
if (!cache.isAlwaysInitialized()) {
continue;
}
setCacheInitialized(frameState, specialization, cache, true);
}
CodeTree updateImplicitCast = createUpdateImplicitCastState(builder, frameState, specialization);
if (updateImplicitCast != null) {
builder.startElseBlock();
builder.tree(createUpdateImplicitCastState(builder, frameState, specialization));
builder.tree(multiState.createSet(frameState, new Object[]{specialization}, true, true));
builder.end();
}
builder.startIf();
if (useDuplicateFlag) {
builder.string(duplicateFoundName);
} else {
builder.string(createSpecializationLocalName(specialization), " != null");
}
builder.end().startBlock();
builder.tree(createCallSpecialization(builder, frameState, executeAndSpecializeType, specialization));
builder.end();
} else {
builder.tree(createCallSpecialization(builder, innerFrameState, executeAndSpecializeType, specialization));
builder.end(innerIfCount.blockCount);
hasFallthrough |= innerIfCount.ifCount > 0;
}
if (pushBoundary) {
builder.end().startFinallyBlock();
GeneratorUtils.popEncapsulatingNode(builder);
builder.end();
builder.end();
}
}
builder.end(outerIfCount.blockCount);
hasFallthrough |= outerIfCount.ifCount > 0;
} else if (mode.isGuardFallback()) {
BlockState ifCount = BlockState.NONE;
if (specialization != null && specialization.getMaximumNumberOfInstances() > 1) {
throw new AssertionError("unsupported path. should be caught by parser..");
}
BlockState innerIfCount = BlockState.NONE;
cachedTriples.addAll(createMethodGuardChecks(frameState, group, guardExpressions, mode));
cachedTriples.addAll(createAssumptionCheckTriples(frameState, specialization, NodeExecutionMode.FALLBACK_GUARD));
cachedTriples = IfTriple.optimize(cachedTriples);
if (specialization != null && !hasImplicitCast) {
IfTriple singleCondition = null;
if (cachedTriples.size() == 1) {
singleCondition = cachedTriples.get(0);
}
if (singleCondition != null) {
int index = cachedTriples.indexOf(singleCondition);
CodeTree stateCheck = multiState.createNotContains(frameState, specializations);
cachedTriples.set(index, new IfTriple(singleCondition.prepare, combineTrees(" && ", stateCheck, singleCondition.condition), singleCondition.statements));
fallbackNeedsState = true;
}
}
innerIfCount = innerIfCount.add(IfTriple.materialize(builder, cachedTriples, false));
prev = visitSpecializationGroupChildren(builder, frameState, prev, group, forType, allowedSpecializations);
if (specialization != null && (prev == null || prev.hasFallthrough())) {
builder.returnFalse();
}
builder.end(innerIfCount.blockCount);
builder.end(ifCount.blockCount);
hasFallthrough |= ifCount.ifCount > 0 || innerIfCount.ifCount > 0;
} else if (mode.isUncached()) {
BlockState ifCount = BlockState.NONE;
if (specialization != null) {
cachedTriples.addAll(createAssumptionCheckTriples(frameState, specialization, NodeExecutionMode.UNCACHED));
}
ifCount = ifCount.add(IfTriple.materialize(builder, IfTriple.optimize(cachedTriples), false));
cachedTriples = createMethodGuardChecks(frameState, group, guardExpressions, mode);
BlockState innerIfCount = IfTriple.materialize(builder, IfTriple.optimize(cachedTriples), false);
prev = visitSpecializationGroupChildren(builder, frameState, prev, group, forType, allowedSpecializations);
if (specialization != null && (prev == null || prev.hasFallthrough())) {
builder.tree(createCallSpecialization(builder, frameState, forType, specialization));
}
builder.end(innerIfCount.blockCount);
builder.end(ifCount.blockCount);
hasFallthrough |= ifCount.ifCount > 0 || innerIfCount.ifCount > 0;
} else {
throw new AssertionError("unexpected path");
}
group.setFallthrough(hasFallthrough);
return builder.build();
}
private SpecializationGroup visitSpecializationGroupChildren(final CodeTreeBuilder builder, FrameState frameState, SpecializationGroup prev, SpecializationGroup group, ExecutableTypeData forType,
Collection<SpecializationData> allowedSpecializations) {
SpecializationGroup currentPrev = prev;
for (SpecializationGroup child : group.getChildren()) {
if (currentPrev != null && !currentPrev.hasFallthrough()) {
break;
}
builder.tree(visitSpecializationGroup(builder, prev, child, forType, frameState.copy(), allowedSpecializations));
currentPrev = child;
}
return currentPrev;
}
private int boundaryIndex = 0;
private final Set<String> usedBoundaryNames = new HashSet<>();
private CodeTreeBuilder (CodeTreeBuilder builder, FrameState frameState, SpecializationData specialization) {
CodeTreeBuilder innerBuilder;
CodeExecutableElement parentMethod = (CodeExecutableElement) builder.findMethod();
String boundaryMethodName;
if (specialization != null) {
boundaryMethodName = specialization.getId() + "Boundary";
} else {
boundaryMethodName = "specializationBoundary";
}
boundaryMethodName = firstLetterLowerCase(boundaryMethodName);
if (usedBoundaryNames.contains(boundaryMethodName)) {
boundaryMethodName = boundaryMethodName + (boundaryIndex++);
}
usedBoundaryNames.add(boundaryMethodName);
String includeFrameParameter = null;
if (specialization != null && specialization.getFrame() != null) {
includeFrameParameter = FRAME_VALUE;
}
CodeExecutableElement boundaryMethod = new CodeExecutableElement(modifiers(PRIVATE), parentMethod.getReturnType(), boundaryMethodName);
GeneratorUtils.mergeSupressWarnings(boundaryMethod, "static-method");
multiState.addParametersTo(frameState, boundaryMethod);
frameState.addParametersTo(boundaryMethod, Integer.MAX_VALUE, includeFrameParameter,
createSpecializationLocalName(specialization));
boundaryMethod.getAnnotationMirrors().add(new CodeAnnotationMirror(types.CompilerDirectives_TruffleBoundary));
boundaryMethod.getThrownTypes().addAll(parentMethod.getThrownTypes());
innerBuilder = boundaryMethod.createBuilder();
((CodeTypeElement) parentMethod.getEnclosingElement()).add(boundaryMethod);
builder.startReturn().startCall("this", boundaryMethod);
multiState.addReferencesTo(frameState, builder);
frameState.addReferencesTo(builder, includeFrameParameter, createSpecializationLocalName(specialization));
builder.end().end();
return innerBuilder;
}
private List<IfTriple> createAssumptionCheckTriples(FrameState frameState, SpecializationData specialization, NodeExecutionMode mode) {
if (specialization == null || specialization.getAssumptionExpressions().isEmpty()) {
return Collections.emptyList();
}
List<IfTriple> triples = new ArrayList<>();
List<AssumptionExpression> assumptions = specialization.getAssumptionExpressions();
for (AssumptionExpression assumption : assumptions) {
CodeTree prepare = null;
CodeTree assumptionReference;
if (mode.isUncached()) {
String localName = assumption.getId();
CodeTreeBuilder builder = new CodeTreeBuilder(null);
CodeTree assumptionInit = writeExpression(frameState, specialization, assumption.getExpression());
builder.declaration(assumption.getExpression().getResolvedType(), localName,
assumptionInit);
prepare = builder.build();
assumptionReference = CodeTreeBuilder.singleString(localName);
} else {
assumptionReference = createAssumptionReference(frameState, specialization, assumption);
}
CodeTree assumptionGuard = createAssumptionGuard(assumptionReference);
CodeTreeBuilder builder = new CodeTreeBuilder(null);
builder.string("(");
builder.tree(assumptionReference).string(" == null || ");
builder.tree(assumptionGuard);
builder.string(")");
triples.add(new IfTriple(prepare, builder.build(), null));
}
return triples;
}
private CodeTree writeExpression(FrameState frameState, SpecializationData specialization, DSLExpression expression) throws AssertionError {
expression.accept(new AbstractDSLExpressionVisitor() {
@Override
public void visitCall(Call binary) {
frameState.addThrownExceptions(binary.getResolvedMethod());
}
});
return DSLExpressionGenerator.write(optimizeExpression(expression), null,
castBoundTypes(bindExpressionValues(frameState, expression, specialization)));
}
private List<IfTriple> createAssumptionSlowPathTriples(FrameState frameState, SpecializationGroup group, AssumptionExpression assumption) throws AssertionError {
List<IfTriple> triples = new ArrayList<>();
LocalVariable var = frameState.get(assumption.getId());
CodeTree declaration = null;
if (var == null) {
triples.addAll(initializeCaches(frameState, frameState.getMode(), group, group.getSpecialization().getBoundCaches(assumption.getExpression(), true), true, false));
CodeTree assumptionExpressions = writeExpression(frameState, group.getSpecialization(), assumption.getExpression());
String name = createAssumptionFieldName(group.getSpecialization(), assumption);
var = new LocalVariable(assumption.getExpression().getResolvedType(), name.substring(0, name.length() - 1), null);
frameState.set(assumption.getId(), var);
declaration = var.createDeclaration(assumptionExpressions);
}
triples.add(new IfTriple(declaration, createAssumptionGuard(var.createReference()), null));
return triples;
}
private CodeTree createDuplicationCheck(CodeTreeBuilder parent, FrameState frameState, SpecializationGroup group, List<GuardExpression> guardExpressions,
boolean useDuplicate, String countName, String duplicateFoundName, String specializationLocalName) {
SpecializationData specialization = group.getSpecialization();
CodeTreeBuilder builder = parent.create();
if (!useDuplicate) {
builder.declaration("int", countName, CodeTreeBuilder.singleString("0"));
}
if (useSpecializationClass(specialization)) {
builder.tree(loadSpecializationClass(frameState, specialization));
}
if (!specialization.hasMultipleInstances()) {
builder.declaration("boolean", duplicateFoundName, CodeTreeBuilder.singleString("false"));
}
FrameState innerFrameState = frameState.copy();
builder.startIf().tree(multiState.createContains(innerFrameState, new Object[]{specialization})).end().startBlock();
if (specialization.hasMultipleInstances()) {
builder.startWhile().string(specializationLocalName, " != null").end().startBlock();
}
List<IfTriple> duplicationtriples = new ArrayList<>();
duplicationtriples.addAll(createMethodGuardChecks(innerFrameState, group, guardExpressions, NodeExecutionMode.FAST_PATH));
duplicationtriples.addAll(createAssumptionCheckTriples(innerFrameState, specialization, NodeExecutionMode.SLOW_PATH));
BlockState duplicationIfCount = IfTriple.materialize(builder, IfTriple.optimize(duplicationtriples), false);
if (useDuplicate) {
builder.startStatement().string(duplicateFoundName, " = true").end();
}
List<CacheExpression> cachesToInitialize = new ArrayList<>();
for (CacheExpression cache : specialization.getCaches()) {
if (!cache.isAlwaysInitialized()) {
continue;
}
if (isCacheInitialized(innerFrameState, specialization, cache)) {
continue;
}
cachesToInitialize.add(cache);
}
if (!cachesToInitialize.isEmpty()) {
List<IfTriple> triples = initializeCaches(innerFrameState, NodeExecutionMode.FAST_PATH, group, cachesToInitialize, true, false);
IfTriple.materialize(builder, IfTriple.optimize(triples), true);
}
if (specialization.hasMultipleInstances()) {
builder.statement("break");
}
builder.end(duplicationIfCount.blockCount);
if (useDuplicate) {
} else {
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.startStatement().string(specializationLocalName, " = ", specializationLocalName, ".next_").end();
} else {
builder.statement(specializationLocalName + " = null");
}
builder.statement(countName + "++");
builder.end();
}
builder.end();
return builder.build();
}
private CodeTree createSpecialize(CodeTreeBuilder parent, FrameState frameState, SpecializationGroup group, SpecializationData specialization) {
CodeTreeBuilder builder = parent.create();
List<IfTriple> triples = new ArrayList<>();
triples.addAll(initializeSpecializationClass(frameState, specialization));
triples.addAll(initializeCaches(frameState, frameState.getMode(), group, specialization.getCaches(), false, true));
triples.addAll(persistAssumptions(frameState, specialization));
triples.addAll(persistSpecializationClass(frameState, specialization));
builder.end(IfTriple.materialize(builder, triples, true).blockCount);
List<SpecializationData> excludesSpecializations = new ArrayList<>();
for (SpecializationData otherSpeciailzation : reachableSpecializations) {
if (otherSpeciailzation == specialization) {
continue;
}
if (otherSpeciailzation.getExcludedBy().contains(specialization)) {
excludesSpecializations.add(otherSpeciailzation);
}
}
if (!excludesSpecializations.isEmpty()) {
SpecializationData[] excludesArray = excludesSpecializations.toArray(new SpecializationData[0]);
builder.tree(exclude.createSet(frameState, excludesArray, true, true));
for (SpecializationData excludes : excludesArray) {
if (useSpecializationClass(excludes)) {
builder.statement("this." + createSpecializationFieldName(excludes) + " = null");
}
}
builder.tree((multiState.createSet(frameState, excludesArray, false, false)));
}
CodeTree updateImplicitCast = createUpdateImplicitCastState(builder, frameState, specialization);
if (updateImplicitCast != null) {
builder.tree(createUpdateImplicitCastState(builder, frameState, specialization));
}
builder.tree(multiState.createSet(frameState, new SpecializationData[]{specialization}, true, true));
return builder.build();
}
private List<IfTriple> persistAssumptions(FrameState frameState, SpecializationData specialization) {
List<IfTriple> triples = new ArrayList<>();
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
LocalVariable var = frameState.get(assumption.getId());
String name = createAssumptionFieldName(specialization, assumption);
CodeTreeBuilder builder = new CodeTreeBuilder(null);
builder.startStatement();
builder.tree(createSpecializationFieldReference(frameState, specialization, name)).string(" = ").tree(var.createReference());
builder.end();
triples.add(new IfTriple(builder.build(), null, null));
}
return triples;
}
private CodeTree loadSpecializationClass(FrameState frameState, SpecializationData specialization) {
if (!useSpecializationClass(specialization)) {
return null;
}
String localName = createSpecializationLocalName(specialization);
String typeName = createSpecializationTypeName(specialization);
LocalVariable var = frameState.get(localName);
CodeTreeBuilder builder = new CodeTreeBuilder(null);
builder.startStatement();
if (var == null) {
builder.string(typeName);
builder.string(" ");
}
builder.string(localName);
builder.string(" = ");
builder.tree(createSpecializationFieldReference(frameState, specialization, null));
builder.end();
if (var == null) {
frameState.set(localName, new LocalVariable(new GeneratedTypeMirror("", typeName), localName, null));
}
return builder.build();
}
private Collection<IfTriple> persistSpecializationClass(FrameState frameState, SpecializationData specialization) {
if (!useSpecializationClass(specialization)) {
return Collections.emptyList();
}
String localName = createSpecializationLocalName(specialization);
LocalVariable var = frameState.get(localName);
if (var == null) {
return Collections.emptyList();
}
String persistFrameState = createSpecializationClassPersisted(specialization);
if (frameState.getBoolean(persistFrameState, false)) {
return Collections.emptyList();
} else {
frameState.setBoolean(persistFrameState, true);
}
CodeTree ref = var.createReference();
CodeTreeBuilder builder = new CodeTreeBuilder(null);
builder.startStatement();
builder.string("this.", createSpecializationFieldName(specialization));
builder.string(" = ");
builder.tree(ref);
builder.end();
return Arrays.asList(new IfTriple(builder.build(), null, null));
}
private static String createSpecializationClassPersisted(SpecializationData specialization) {
return createSpecializationLocalName(specialization) + "$persisted";
}
private static void assertSpecializationClassNotInitialized(FrameState frameState, SpecializationData specialization) {
String framestateVarName = createSpecializationClassInitialized(specialization);
if (frameState.get(framestateVarName) != null) {
throw new AssertionError("Specialization class already initialized. " + specialization);
}
}
private Collection<? extends IfTriple> initializeSpecializationClass(FrameState frameState, SpecializationData specialization) {
boolean useSpecializationClass = useSpecializationClass(specialization);
if (useSpecializationClass) {
String localName = createSpecializationLocalName(specialization);
String typeName = createSpecializationTypeName(specialization);
String classInitialized = createSpecializationClassInitialized(specialization);
if (!frameState.getBoolean(classInitialized, false)) {
GeneratedTypeMirror type = new GeneratedTypeMirror("", typeName);
CodeTreeBuilder initBuilder = new CodeTreeBuilder(null);
boolean isNode = specializationClassIsNode(specialization);
if (isNode) {
initBuilder.startCall("super", "insert");
}
initBuilder.startNew(typeName);
if (specialization.getMaximumNumberOfInstances() > 1) {
initBuilder.string(createSpecializationFieldName(specialization));
}
initBuilder.end();
if (isNode) {
initBuilder.end();
}
CodeTree init = initBuilder.build();
CodeTreeBuilder builder = new CodeTreeBuilder(null);
builder.startStatement();
if (frameState.get(localName) == null) {
builder.string(typeName);
builder.string(" ");
}
builder.string(localName);
builder.string(" = ");
builder.tree(init);
builder.end();
frameState.setBoolean(classInitialized, true);
frameState.set(localName, new LocalVariable(type, localName, CodeTreeBuilder.singleString(localName)));
return Arrays.asList(new IfTriple(builder.build(), null, null));
}
}
return Collections.emptyList();
}
private static String createSpecializationClassInitialized(SpecializationData specialization) {
return createSpecializationLocalName(specialization) + "$initialized";
}
private CodeTree createUpdateImplicitCastState(CodeTreeBuilder parent, FrameState frameState, SpecializationData specialization) {
CodeTreeBuilder builder = null;
int signatureIndex = 0;
for (Parameter p : specialization.getSignatureParameters()) {
TypeMirror targetType = p.getType();
TypeMirror polymorphicType = node.getPolymorphicSpecialization().findParameterOrDie(p.getSpecification().getExecution()).getType();
if (typeSystem.hasImplicitSourceTypes(targetType) && needsCastTo(polymorphicType, targetType)) {
String implicitFieldName = createImplicitTypeStateLocalName(p);
if (builder == null) {
builder = parent.create();
}
builder.tree(multiState.createSetInteger(frameState, new TypeGuard(p.getType(), signatureIndex), CodeTreeBuilder.singleString(implicitFieldName)));
}
signatureIndex++;
}
return builder == null ? null : builder.build();
}
private CodeTree createAssumptionGuard(CodeTree assumptionValue) {
return CodeTreeBuilder.createBuilder().startStaticCall(types.Assumption, "isValidAssumption").tree(assumptionValue).end().build();
}
private static CodeTree combineTrees(String sep, CodeTree... trees) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
String s = "";
for (CodeTree tree : trees) {
if (tree != null && !tree.isEmpty()) {
if (sep != null) {
builder.string(s);
}
builder.tree(tree);
s = sep;
}
}
return builder.build();
}
private CodeTree createFastPathAssumptionCheck(CodeTreeBuilder parent, SpecializationData specialization, ExecutableTypeData forType, FrameState frameState)
throws AssertionError {
CodeTreeBuilder builder = parent.create();
builder.startIf();
String sep = "";
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
builder.string(sep);
builder.string("!");
builder.tree(createAssumptionGuard(createAssumptionReference(frameState, specialization, assumption)));
sep = " || ";
}
builder.end().startBlock();
builder.tree(createTransferToInterpreterAndInvalidate());
builder.tree(createRemoveThis(builder, frameState, forType, specialization));
builder.end();
return builder.build();
}
private static CodeTree createTryExecuteChild(LocalVariable value, CodeTree executeChild, boolean needDeclaration, boolean hasTry) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
boolean hasDeclaration = false;
if ((hasTry || !executeChild.isSingleLine()) && needDeclaration) {
builder.tree(value.createDeclaration(null));
hasDeclaration = true;
}
if (hasTry) {
builder.startTryBlock();
} else {
builder.startGroup();
}
if (executeChild.isSingleLine()) {
builder.startStatement();
if (hasDeclaration || !needDeclaration) {
builder.tree(executeChild);
} else {
builder.type(value.getTypeMirror()).string(" ").tree(executeChild);
}
builder.end();
} else {
builder.tree(executeChild);
}
builder.end();
return builder.build();
}
private ExecutableTypeData resolveTargetExecutable(NodeExecutionData execution, TypeMirror target) {
NodeChildData child = execution.getChild();
if (child == null) {
return null;
}
ExecutableTypeData targetExecutable = child.findExecutableType(target);
if (targetExecutable == null) {
targetExecutable = child.findAnyGenericExecutableType(context);
}
return targetExecutable;
}
private CodeTree createCatchRewriteException(CodeTreeBuilder parent, SpecializationData specialization, ExecutableTypeData forType, FrameState frameState, CodeTree execution) {
if (specialization.getExceptions().isEmpty()) {
return execution;
}
CodeTreeBuilder builder = parent.create();
builder.startTryBlock();
builder.tree(execution);
boolean nonSlowPath = false;
TypeMirror[] exceptionTypes = new TypeMirror[specialization.getExceptions().size()];
for (int i = 0; i < exceptionTypes.length; i++) {
TypeMirror type = specialization.getExceptions().get(i).getJavaClass();
if (!isAssignable(type, types.SlowPathException) && !isAssignable(type, context.getType(ArithmeticException.class))) {
nonSlowPath = true;
}
exceptionTypes[i] = type;
}
builder.end().startCatchBlock(exceptionTypes, "ex");
if (nonSlowPath) {
builder.tree(createTransferToInterpreterAndInvalidate());
} else {
builder.lineComment("implicit transferToInterpreterAndInvalidate()");
}
builder.tree(createExcludeThis(builder, frameState, forType, specialization));
builder.end();
return builder.build();
}
private Map<SpecializationData, CodeExecutableElement> removeThisMethods = new HashMap<>();
private CodeTree createExcludeThis(CodeTreeBuilder parent, FrameState frameState, ExecutableTypeData forType, SpecializationData specialization) {
CodeTreeBuilder builder = parent.create();
if (!frameState.getMode().isSlowPath()) {
builder.declaration(context.getType(Lock.class), "lock", "getLock()");
}
if (needsSpecializeLocking) {
builder.statement("lock.lock()");
builder.startTryBlock();
}
SpecializationData[] specializations;
if (specialization.getUncachedSpecialization() != null) {
specializations = new SpecializationData[]{specialization, specialization.getUncachedSpecialization()};
} else {
specializations = new SpecializationData[]{specialization};
}
builder.tree(this.exclude.createSet(null, specializations, true, true));
builder.tree(this.multiState.createSet(null, specializations, false, true));
for (SpecializationData removeSpecialization : specializations) {
if (useSpecializationClass(removeSpecialization)) {
String fieldName = createSpecializationFieldName(removeSpecialization);
builder.statement("this." + fieldName + " = null");
}
}
if (needsSpecializeLocking) {
builder.end().startFinallyBlock();
builder.statement("lock.unlock()");
builder.end();
}
boolean hasUnexpectedResultRewrite = specialization.hasUnexpectedResultRewrite();
boolean hasReexecutingRewrite = !hasUnexpectedResultRewrite || specialization.getExceptions().size() > 1;
if (hasReexecutingRewrite) {
if (hasUnexpectedResultRewrite) {
builder.startIf().string("ex").instanceOf(types.UnexpectedResultException).end().startBlock();
builder.tree(createReturnUnexpectedResult(forType, true));
builder.end().startElseBlock();
builder.tree(createCallExecuteAndSpecialize(forType, frameState));
builder.end();
} else {
builder.tree(createCallExecuteAndSpecialize(forType, frameState));
}
} else {
assert hasUnexpectedResultRewrite;
builder.tree(createReturnUnexpectedResult(forType, false));
}
builder.end();
return builder.build();
}
private CodeTree createRemoveThis(CodeTreeBuilder parent, FrameState frameState, ExecutableTypeData forType, SpecializationData specialization) {
CodeExecutableElement method = removeThisMethods.get(specialization);
String specializationLocalName = createSpecializationLocalName(specialization);
boolean useSpecializationClass = useSpecializationClass(specialization);
if (method == null) {
method = new CodeExecutableElement(context.getType(void.class), "remove" + specialization.getId() + "_");
if (useSpecializationClass) {
method.addParameter(new CodeVariableElement(context.getType(Object.class), specializationLocalName));
}
CodeTreeBuilder builder = method.createBuilder();
if (needsSpecializeLocking) {
builder.declaration(context.getType(Lock.class), "lock", "getLock()");
builder.statement("lock.lock()");
builder.startTryBlock();
}
String fieldName = createSpecializationFieldName(specialization);
if (!useSpecializationClass || specialization.getMaximumNumberOfInstances() == 1) {
builder.tree((multiState.createSet(null, new Object[]{specialization}, false, true)));
if (useSpecializationClass) {
builder.statement("this." + fieldName + " = null");
}
} else {
String typeName = createSpecializationTypeName(specialization);
boolean specializedIsNode = specializationClassIsNode(specialization);
builder.declaration(typeName, "prev", "null");
builder.declaration(typeName, "cur", "this." + fieldName);
builder.startWhile();
builder.string("cur != null");
builder.end().startBlock();
builder.startIf().string("cur == ").string(specializationLocalName).end().startBlock();
builder.startIf().string("prev == null").end().startBlock();
builder.statement("this." + fieldName + " = cur.next_");
if (specializedIsNode) {
builder.statement("this.adoptChildren()");
}
builder.end().startElseBlock();
builder.statement("prev.next_ = cur.next_");
if (specializedIsNode) {
builder.statement("prev.adoptChildren()");
}
builder.end();
builder.statement("break");
builder.end();
builder.statement("prev = cur");
builder.statement("cur = cur.next_");
builder.end();
builder.startIf().string("this." + fieldName).string(" == null").end().startBlock();
builder.tree((multiState.createSet(null, Arrays.asList(specialization).toArray(new SpecializationData[0]), false, true)));
builder.end();
}
if (needsSpecializeLocking) {
builder.end().startFinallyBlock();
builder.statement("lock.unlock()");
builder.end();
}
removeThisMethods.put(specialization, method);
}
CodeTreeBuilder builder = parent.create();
builder.startStatement().startCall(method.getSimpleName().toString());
if (useSpecializationClass) {
builder.string(specializationLocalName);
}
builder.end().end();
builder.tree(createCallExecuteAndSpecialize(forType, frameState));
return builder.build();
}
private CodeTree createCallExecute(ExecutableTypeData forType, ExecutableTypeData targetType, FrameState frameState) {
TypeMirror returnType = targetType.getReturnType();
List<CodeTree> bindings = new ArrayList<>();
List<TypeMirror> sourceTypes = forType.getSignatureParameters();
List<TypeMirror> targetTypes = targetType.getSignatureParameters();
if (sourceTypes.size() != targetTypes.size()) {
throw new IllegalArgumentException();
}
if (targetType.getFrameParameter() != null) {
LocalVariable parameterLocal = frameState.get(FRAME_VALUE);
TypeMirror parameterTargetType = targetType.getFrameParameter();
if (parameterLocal == null) {
bindings.add(CodeTreeBuilder.createBuilder().defaultValue(parameterTargetType).build());
} else {
bindings.add(parameterLocal.createReference());
}
}
for (int i = 0; i < sourceTypes.size(); i++) {
LocalVariable parameterLocal = frameState.getValue(i);
TypeMirror parameterTargetType = targetTypes.get(i);
if (parameterLocal == null) {
bindings.add(CodeTreeBuilder.createBuilder().defaultValue(parameterTargetType).build());
} else {
bindings.add(parameterLocal.createReference());
}
}
CodeTree call = callMethod(frameState, null, targetType.getMethod(), bindings.toArray(new CodeTree[0]));
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder = builder.create();
if (isVoid(forType.getReturnType())) {
builder.statement(call);
builder.returnStatement();
} else {
builder.startReturn();
builder.tree(expectOrCast(returnType, forType, call));
builder.end();
}
return builder.build();
}
private CodeTree createCallExecuteAndSpecialize(ExecutableTypeData forType, FrameState frameState) {
TypeMirror returnType = node.getPolymorphicSpecialization().getReturnType().getType();
String frame = null;
if (needsFrameToExecute(reachableSpecializations)) {
frame = FRAME_VALUE;
}
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startCall(createExecuteAndSpecializeName());
frameState.addReferencesTo(builder, frame);
builder.end();
CodeTree call = builder.build();
builder = builder.create();
if (isVoid(forType.getReturnType())) {
builder.statement(call);
builder.returnStatement();
} else {
builder.startReturn();
builder.tree(expectOrCast(returnType, forType, call));
builder.end();
}
return builder.build();
}
private CodeTree createReturnUnexpectedResult(ExecutableTypeData forType, boolean needsCast) {
TypeMirror returnType = context.getType(Object.class);
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startCall(needsCast ? "((UnexpectedResultException) ex)" : "ex", "getResult").end();
CodeTree call = builder.build();
builder = builder.create();
if (isVoid(forType.getReturnType())) {
builder.statement(call);
builder.returnStatement();
} else {
builder.startReturn();
builder.tree(expectOrCast(returnType, forType, call));
builder.end();
}
return builder.build();
}
private List<IfTriple> createMethodGuardChecks(FrameState frameState, SpecializationGroup group, List<GuardExpression> guardExpressions, NodeExecutionMode mode) {
List<IfTriple> triples = new ArrayList<>();
for (GuardExpression guard : guardExpressions) {
if (mode.isSlowPath() && !guard.isConstantTrueInSlowPath(context, mode.isUncached())) {
CodeTreeBuilder builder = new CodeTreeBuilder(null);
List<IfTriple> innerTriples = new ArrayList<>();
boolean guardStateBit = guardNeedsStateBit(group.getSpecialization(), guard);
FrameState innerFrameState = frameState;
if (guardStateBit) {
if (group.getSpecialization() == null) {
throw new AssertionError();
}
innerFrameState = frameState.copy();
builder.startIf().tree(multiState.createNotContains(innerFrameState, new Object[]{guard})).end().startBlock();
innerTriples.addAll(initializeSpecializationClass(innerFrameState, group.getSpecialization()));
innerTriples.addAll(persistSpecializationClass(innerFrameState, group.getSpecialization()));
}
boolean store = !guardStateBit;
Set<CacheExpression> boundCaches = group.getSpecialization().getBoundCaches(guard.getExpression(), true);
innerTriples.addAll(initializeCaches(innerFrameState, mode, group, boundCaches, store, guardStateBit));
innerTriples.addAll(initializeCasts(innerFrameState, group, guard.getExpression(), mode));
IfTriple.materialize(builder, innerTriples, true);
if (guardStateBit) {
builder.tree(multiState.createSet(innerFrameState, new Object[]{guard}, true, true));
builder.end();
}
triples.add(new IfTriple(builder.build(), null, null));
} else if (mode.isGuardFallback()) {
triples.addAll(initializeCasts(frameState, group, guard.getExpression(), mode));
} else if (mode.isFastPath()) {
triples.addAll(initializeCaches(frameState, mode, group, group.getSpecialization().getBoundCaches(guard.getExpression(), true), true, false));
}
triples.add(createMethodGuardCheck(frameState, group.getSpecialization(), guard, mode));
}
return triples;
}
private List<IfTriple> initializeCaches(FrameState frameState, NodeExecutionMode mode, SpecializationGroup group, Collection<CacheExpression> caches, boolean store, boolean forcePersist) {
if (group.getSpecialization() == null || caches.isEmpty()) {
return Collections.emptyList();
}
List<IfTriple> triples = new ArrayList<>();
for (CacheExpression cache : caches) {
if (cache.isEagerInitialize()) {
continue;
} else if (mode.isFastPath() && !cache.isAlwaysInitialized()) {
continue;
} else if (mode.isUncached() && cache.isWeakReference()) {
continue;
}
boolean useStore = store;
if (cache.isAlwaysInitialized()) {
useStore = true;
}
triples.addAll(initializeCasts(frameState, group, cache.getDefaultExpression(), mode));
triples.addAll(persistAndInitializeCache(frameState, group.getSpecialization(), cache, useStore, forcePersist));
}
return triples;
}
private Collection<IfTriple> persistAndInitializeCache(FrameState frameState, SpecializationData specialization, CacheExpression cache, boolean store, boolean persist) {
List<IfTriple> triples = new ArrayList<>();
triples.addAll(initializeReferences(frameState, cache));
CodeTree init = initializeCache(frameState, specialization, cache);
if (store) {
triples.addAll(storeCache(frameState, specialization, cache, init));
}
if (persist) {
triples.addAll(persistCache(frameState, specialization, cache, init));
}
return triples;
}
private Collection<IfTriple> persistCache(FrameState frameState, SpecializationData specialization, CacheExpression cache, CodeTree cacheValue) {
if (cache.isAlwaysInitialized()) {
return Collections.emptyList();
} else {
List<IfTriple> triples = new ArrayList<>();
String name = createFieldName(specialization, cache.getParameter());
LocalVariable local = frameState.get(name);
CodeTree value;
if (local != null) {
value = local.createReference();
} else if (cacheValue == null) {
return Collections.emptyList();
} else {
value = cacheValue;
}
TypeMirror type = cache.getParameter().getType();
String frameStateInitialized = name + "$initialized";
if (frameState.getBoolean(frameStateInitialized, false)) {
return Collections.emptyList();
} else {
frameState.setBoolean(frameStateInitialized, true);
}
CodeTreeBuilder builder = new CodeTreeBuilder(null);
Parameter parameter = cache.getParameter();
boolean useSpecializationClass = useSpecializationClass(specialization);
String insertTarget;
if (useSpecializationClass) {
insertTarget = createSpecializationLocalName(specialization);
} else {
insertTarget = "super";
}
TypeMirror nodeType = types.Node;
TypeMirror nodeArrayType = new ArrayCodeTypeMirror(types.Node);
boolean isNode = isAssignable(parameter.getType(), nodeType);
boolean isNodeInterface = isNode || isAssignable(type, types.NodeInterface);
boolean isNodeArray = isAssignable(type, nodeArrayType);
boolean isNodeInterfaceArray = isNodeArray || isNodeInterfaceArray(type);
if (isNodeInterface || isNodeInterfaceArray) {
builder = new CodeTreeBuilder(null);
String insertName;
if (cache.isAdopt()) {
insertName = useSpecializationClass ? useInsertAccessor(specialization, isNodeInterfaceArray) : "insert";
} else {
insertName = null;
}
final TypeMirror castType;
if (isNodeInterface) {
if (isNode) {
castType = null;
} else {
castType = nodeType;
}
} else {
assert isNodeInterfaceArray;
if (isNodeArray) {
castType = null;
} else {
castType = nodeArrayType;
}
}
if (castType == null) {
CodeTreeBuilder noCast = new CodeTreeBuilder(null);
if (cache.isAdopt()) {
noCast.startCall(insertTarget, insertName);
}
noCast.tree(value);
if (cache.isAdopt()) {
noCast.end();
}
value = noCast.build();
} else {
String fieldName = createFieldName(specialization, cache.getParameter()) + "__";
builder.declaration(cache.getDefaultExpression().getResolvedType(), fieldName, value);
if (cache.isAdopt()) {
builder.startIf().string(fieldName).instanceOf(castType).end().startBlock();
builder.startStatement();
builder.startCall(insertTarget, insertName);
builder.startGroup().cast(castType).string(fieldName).end();
builder.end().end();
}
builder.end();
value = CodeTreeBuilder.singleString(fieldName);
}
}
CodeTree cacheReference = createCacheReference(frameState, specialization, cache);
if (!cache.isEagerInitialize() && sharedCaches.containsKey(cache) && !ElementUtils.isPrimitive(cache.getParameter().getType())) {
builder.startIf().tree(cacheReference).string(" == null").end().startBlock();
builder.startStatement().tree(cacheReference).string(" = ").tree(value).end();
builder.end();
} else {
builder.startStatement().tree(cacheReference).string(" = ").tree(value).end();
}
triples.add(new IfTriple(builder.build(), null, null));
return triples;
}
}
private final Map<String, Integer> uniqueSupplierLocalIndexes = new HashMap<>();
private List<IfTriple> initializeReferences(FrameState frameState, CacheExpression cache) {
if (isSupplierInitialized(frameState, cache)) {
return Collections.emptyList();
}
String supplierName = createElementReferenceName(cache);
String supplierLocalName = supplierName + "_";
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
int index = uniqueSupplierLocalIndexes.getOrDefault(supplierLocalName, 0);
uniqueSupplierLocalIndexes.put(supplierLocalName, index + 1);
if (index > 0) {
supplierLocalName = supplierLocalName + index;
}
String method = cache.isCachedContext() ? "super.lookupContextReference" : "super.lookupLanguageReference";
if (frameState.getMode().isSlowPath()) {
builder.declaration(cache.getReferenceType(), supplierLocalName, "this." + supplierName);
builder.startIf().string(supplierLocalName).string(" == null").end().startBlock();
builder.startStatement().string("this.", supplierName).string(" = ").string(supplierLocalName).string(" = ").startCall(method).typeLiteral(cache.getLanguageType()).end().end();
builder.end();
} else {
builder.startStatement().type(cache.getReferenceType()).string(" ", supplierLocalName).string(" = ").string("this.", supplierName).end();
}
setSupplierInitialized(frameState, cache, true);
frameState.set(supplierName, new LocalVariable(cache.getReferenceType(), supplierLocalName, null));
return Arrays.asList(new IfTriple(builder.build(), null, null));
}
private static boolean isSupplierInitialized(FrameState frameState, CacheExpression cache) {
if (!cache.isCachedContext() && !cache.isCachedLanguage()) {
return true;
}
String supplierName = createElementReferenceName(cache);
String supplierInitialized = supplierName + "$initialized";
return frameState.getBoolean(supplierInitialized, false);
}
private static void setSupplierInitialized(FrameState frameState, CacheExpression cache, boolean initialized) {
if (!cache.isCachedContext() && !cache.isCachedLanguage()) {
return;
}
String supplierName = createElementReferenceName(cache);
String supplierInitialized = supplierName + "$initialized";
frameState.setBoolean(supplierInitialized, initialized);
}
private Map<String, List<Parameter>> uniqueCachedParameterLocalNames = new HashMap<>();
private Collection<IfTriple> storeCache(FrameState frameState, SpecializationData specialization, CacheExpression cache, CodeTree value) {
if (value == null) {
return Collections.emptyList();
}
if (isCacheInitialized(frameState, specialization, cache)) {
return Collections.emptyList();
}
TypeMirror type = cache.getParameter().getType();
CodeTreeBuilder builder = new CodeTreeBuilder(null);
String refName = createCacheLocalName(specialization, cache);
CodeTree useValue;
if ((ElementUtils.isAssignable(type, types.Node) || ElementUtils.isAssignable(type, new ArrayCodeTypeMirror(types.Node))) &&
(!cache.isAlwaysInitialized()) && cache.isAdopt()) {
useValue = builder.create().startCall("super.insert").tree(value).end().build();
} else {
useValue = value;
}
if (cache.isAlwaysInitialized() && frameState.getMode().isSlowPath()) {
builder.startStatement().string(refName, " = ").tree(useValue).end();
} else {
builder.declaration(type, refName, useValue);
}
setCacheInitialized(frameState, specialization, cache, true);
List<IfTriple> triples = new ArrayList<>();
triples.add(new IfTriple(builder.build(), null, null));
return triples;
}
private boolean isCacheInitialized(FrameState frameState, SpecializationData specialization, CacheExpression cache) {
String name = createFieldName(specialization, cache.getParameter());
return frameState.get(name) != null;
}
private void setCacheInitialized(FrameState frameState, SpecializationData specialization, CacheExpression cache, boolean initialized) {
String name = createFieldName(specialization, cache.getParameter());
if (initialized) {
frameState.set(name, new LocalVariable(cache.getParameter().getType(), name,
CodeTreeBuilder.singleString(createCacheLocalName(specialization, cache))));
} else {
frameState.set(name, null);
setSupplierInitialized(frameState, cache, false);
}
}
private String createCacheLocalName(SpecializationData specialization, CacheExpression cache) {
String name = createFieldName(specialization, cache.getParameter());
String refName = name + "_";
List<Parameter> variables = uniqueCachedParameterLocalNames.computeIfAbsent(refName, (v) -> new ArrayList<>());
int index = variables.indexOf(cache.getParameter());
if (index == -1) {
index = variables.size();
variables.add(cache.getParameter());
}
if (index != 0) {
refName = name + "_" + index;
}
return refName;
}
private CodeTree initializeCache(FrameState frameState, SpecializationData specialization, CacheExpression cache) {
String name = createFieldName(specialization, cache.getParameter());
if (frameState.get(name) != null) {
return null;
}
CodeTree tree;
if (cache.isMergedLibrary()) {
if (frameState.getMode().isUncached()) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.staticReference(createLibraryConstant(libraryConstants, cache.getParameter().getType()));
builder.startCall(".getUncached");
builder.tree(writeExpression(frameState, specialization, cache.getDefaultExpression()));
builder.end();
tree = builder.build();
} else {
tree = CodeTreeBuilder.singleString("this." + cache.getMergedLibraryIdentifier());
}
} else if (cache.isCachedContext() || cache.isCachedLanguage()) {
String fieldName = createElementReferenceName(cache);
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
LocalVariable var = frameState.get(fieldName);
if (var != null) {
builder.tree(var.createReference());
} else {
builder.string("this.").string(fieldName);
}
if (!cache.isReference()) {
builder.string(".get()");
}
tree = builder.build();
} else {
DSLExpression expression;
if (frameState.getMode().isUncached()) {
expression = cache.getUncachedExpression();
} else {
expression = cache.getDefaultExpression();
if (specialization.needsTruffleBoundary() &&
(specialization.isAnyLibraryBoundInGuard() || specialization.needsVirtualFrame())) {
expression = substituteToDispatchedUncached(expression);
}
}
tree = writeExpression(frameState, specialization, expression);
}
return tree;
}
private DSLExpression substituteToDispatchedUncached(DSLExpression expression) {
return expression.reduce(new DSLExpressionReducer() {
public DSLExpression visitVariable(Variable binary) {
return binary;
}
public DSLExpression visitNegate(Negate negate) {
return negate;
}
public DSLExpression visitCall(Call call) {
if (call.getName().equals("getUncached") && ElementUtils.typeEquals(call.getResolvedMethod().getEnclosingElement().asType(), types.LibraryFactory)) {
Call newCall = new Call(call.getReceiver(), call.getName(), Collections.emptyList());
newCall.setResolvedMethod(ElementUtils.findExecutableElement(types.LibraryFactory, "getUncached", 0));
newCall.setResolvedTargetType(call.getResolvedTargetType());
return newCall;
}
return call;
}
public DSLExpression visitBinary(Binary binary) {
return binary;
}
});
}
private static String createElementReferenceName(CacheExpression cache) {
if (cache.isCachedContext()) {
return ElementUtils.firstLetterLowerCase(ElementUtils.getSimpleName(cache.getLanguageType())) + "ContextReference_";
} else if (cache.isCachedLanguage()) {
return ElementUtils.firstLetterLowerCase(ElementUtils.getSimpleName(cache.getLanguageType())) + "Reference_";
} else {
throw new AssertionError();
}
}
private IfTriple createMethodGuardCheck(FrameState frameState, SpecializationData specialization, GuardExpression guard, NodeExecutionMode mode) {
DSLExpression expression = optimizeExpression(guard.getExpression());
CodeTree init = null;
CodeTree expressionCode = writeExpression(frameState, specialization, expression);
if (mode.isGuardFallback()) {
GuardExpression guardWithBit = getGuardThatNeedsStateBit(specialization, guard);
if (guardWithBit != null) {
CodeTreeBuilder builder = new CodeTreeBuilder(null);
builder.string("(");
builder.tree(multiState.createNotContains(frameState, new Object[]{guardWithBit}));
builder.string(" || ");
builder.tree(expressionCode);
builder.string(")");
expressionCode = builder.build();
fallbackNeedsState = true;
}
}
CodeTree assertion = null;
if (mode.isFastPath() || mode.isGuardFallback()) {
if (!specialization.isDynamicParameterBound(expression, true) && !guard.isWeakReferenceGuard()) {
assertion = CodeTreeBuilder.createBuilder().startAssert().tree(expressionCode).end().build();
expressionCode = null;
}
} else {
if (guard.isConstantTrueInSlowPath(context, mode.isUncached())) {
assertion = CodeTreeBuilder.createBuilder().startStatement().string("// assert ").tree(expressionCode).end().build();
expressionCode = null;
}
}
return new IfTriple(init, expressionCode, assertion);
}
private static Map<Variable, CodeTree> castBoundTypes(Map<Variable, LocalVariable> bindings) {
Map<Variable, CodeTree> resolvedBindings = new HashMap<>();
for (Variable variable : bindings.keySet()) {
LocalVariable localVariable = bindings.get(variable);
CodeTree resolved = localVariable.createReference();
TypeMirror sourceType = localVariable.getTypeMirror();
TypeMirror targetType = variable.getResolvedTargetType();
if (targetType == null) {
targetType = variable.getResolvedType();
}
if (!isAssignable(sourceType, targetType)) {
resolved = CodeTreeBuilder.createBuilder().startParantheses().cast(targetType, resolved).end().build();
}
resolvedBindings.put(variable, resolved);
}
return resolvedBindings;
}
private Map<Variable, LocalVariable> bindExpressionValues(FrameState frameState, DSLExpression expression, SpecializationData specialization) throws AssertionError {
Map<Variable, LocalVariable> bindings = new HashMap<>();
Set<Variable> boundVariables = expression.findBoundVariables();
if (specialization == null && !boundVariables.isEmpty()) {
throw new AssertionError("Cannot bind guard variable in non-specialization group. yet.");
}
for (Variable variable : boundVariables) {
Parameter resolvedParameter = specialization.findByVariable(variable.getResolvedVariable());
if (resolvedParameter != null) {
LocalVariable localVariable = bindExpressionVariable(frameState, specialization, resolvedParameter);
if (localVariable != null) {
bindings.put(variable, localVariable);
}
}
}
return bindings;
}
private LocalVariable bindExpressionVariable(FrameState frameState, SpecializationData specialization, Parameter resolvedParameter) {
LocalVariable localVariable;
if (resolvedParameter.getSpecification().isCached()) {
String cachedMemberName = createFieldName(specialization, resolvedParameter);
localVariable = frameState.get(cachedMemberName);
CodeTree ref;
if (localVariable == null) {
CacheExpression cache = specialization.findCache(resolvedParameter);
ref = createCacheReference(frameState, specialization, cache);
} else {
ref = localVariable.createReference();
}
localVariable = new LocalVariable(resolvedParameter.getType(), cachedMemberName, ref);
} else {
if (resolvedParameter.getSpecification().isSignature()) {
NodeExecutionData execution = resolvedParameter.getSpecification().getExecution();
localVariable = frameState.getValue(execution);
} else {
localVariable = frameState.get(resolvedParameter.getLocalName());
}
}
return localVariable;
}
private CodeTree createSpecializationFieldReference(FrameState frameState, SpecializationData s, String fieldName) {
CodeTreeBuilder builder = new CodeTreeBuilder(null);
if (useSpecializationClass(s)) {
String localName = createSpecializationLocalName(s);
LocalVariable var = frameState.get(localName);
if (var != null) {
builder.string(localName);
} else {
builder.string("this.", createSpecializationFieldName(s));
}
} else {
builder.string("this");
}
if (fieldName != null) {
builder.string(".");
builder.string(fieldName);
}
return builder.build();
}
private CodeTree createCacheReference(FrameState frameState, SpecializationData specialization, CacheExpression cache) {
if (cache == null) {
return CodeTreeBuilder.singleString("null /* cache not resolved */");
}
if (frameState.getMode().isUncached()) {
return initializeCache(frameState, specialization, cache);
} else {
if (cache.isAlwaysInitialized()) {
return initializeCache(frameState, specialization, cache);
} else {
String sharedName = sharedCaches.get(cache);
CodeTree ref;
if (sharedName != null) {
ref = CodeTreeBuilder.createBuilder().string("this.").string(sharedName).build();
} else {
String cacheFieldName = createFieldName(specialization, cache.getParameter());
ref = createSpecializationFieldReference(frameState, specialization, cacheFieldName);
}
return ref;
}
}
}
private CodeTree createAssumptionReference(FrameState frameState, SpecializationData s, AssumptionExpression a) {
String assumptionFieldName = createAssumptionFieldName(s, a);
return createSpecializationFieldReference(frameState, s, assumptionFieldName);
}
private IfTriple createTypeCheckOrCast(FrameState frameState, SpecializationGroup group, TypeGuard typeGuard,
NodeExecutionMode specializationExecution, boolean castOnly, boolean forceImplicitCast) {
CodeTreeBuilder prepareBuilder = CodeTreeBuilder.createBuilder();
CodeTreeBuilder checkBuilder = CodeTreeBuilder.createBuilder();
int signatureIndex = typeGuard.getSignatureIndex();
LocalVariable value = frameState.getValue(signatureIndex);
TypeMirror targetType = typeGuard.getType();
if (!needsCastTo(value.getTypeMirror(), targetType)) {
TypeMirror genericTargetType = node.getGenericSpecialization().findParameterOrDie(node.getChildExecutions().get(signatureIndex)).getType();
if (typeEquals(value.getTypeMirror(), genericTargetType)) {
return null;
}
boolean foundImplicitSubType = false;
if (forceImplicitCast) {
List<ImplicitCastData> casts = typeSystem.lookupByTargetType(targetType);
for (ImplicitCastData cast : casts) {
if (isSubtype(cast.getSourceType(), targetType)) {
foundImplicitSubType = true;
break;
}
}
}
if (!foundImplicitSubType) {
return null;
}
}
NodeExecutionData execution = node.getChildExecutions().get(signatureIndex);
CodeTreeBuilder castBuilder = prepareBuilder.create();
List<ImplicitCastData> sourceTypes = typeSystem.lookupByTargetType(targetType);
CodeTree valueReference = value.createReference();
if (sourceTypes.isEmpty()) {
checkBuilder.tree(TypeSystemCodeGenerator.check(typeSystem, targetType, valueReference));
castBuilder.tree(TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference));
} else {
List<SpecializationData> specializations = group.collectSpecializations();
List<Parameter> parameters = new ArrayList<>();
for (SpecializationData otherSpecialization : specializations) {
parameters.add(otherSpecialization.findParameterOrDie(execution));
}
if (specializationExecution.isFastPath() || specializationExecution.isGuardFallback() || specializationExecution.isUncached()) {
CodeTree implicitState;
if (specializationExecution.isGuardFallback() || specializationExecution.isUncached()) {
implicitState = null;
} else {
implicitState = multiState.createExtractInteger(frameState, typeGuard);
}
checkBuilder.tree(TypeSystemCodeGenerator.implicitCheckFlat(typeSystem, targetType, valueReference, implicitState));
castBuilder.tree(TypeSystemCodeGenerator.implicitCastFlat(typeSystem, targetType, valueReference, implicitState));
} else {
Parameter parameter = parameters.get(0);
String implicitStateName = createImplicitTypeStateLocalName(parameter);
CodeTree defaultValue = null;
prepareBuilder.declaration(context.getType(int.class), implicitStateName, defaultValue);
CodeTree specializeCall = TypeSystemCodeGenerator.implicitSpecializeFlat(typeSystem, targetType, valueReference);
checkBuilder.startParantheses();
checkBuilder.string(implicitStateName, " = ").tree(specializeCall);
checkBuilder.end();
checkBuilder.string(" != 0");
castBuilder.tree(TypeSystemCodeGenerator.implicitCastFlat(typeSystem, targetType, valueReference, CodeTreeBuilder.singleString(implicitStateName)));
}
}
if (castOnly) {
LocalVariable currentValue = frameState.getValue(execution);
CodeTreeBuilder localsBuilder = CodeTreeBuilder.createBuilder();
LocalVariable castVariable = currentValue.nextName().newType(typeGuard.getType()).accessWith(null);
frameState.setValue(execution, castVariable);
localsBuilder.tree(castVariable.createDeclaration(castBuilder.build()));
return new IfTriple(localsBuilder.build(), null, null);
} else {
return new IfTriple(prepareBuilder.build(), checkBuilder.build(), null);
}
}
private List<IfTriple> initializeCasts(FrameState frameState, SpecializationGroup group, DSLExpression expression, NodeExecutionMode specializationExecution) {
Set<VariableElement> boundElements = expression.findBoundVariableElements();
if (boundElements.isEmpty()) {
return Collections.emptyList();
}
List<IfTriple> triples = new ArrayList<>();
for (VariableElement variable : boundElements) {
Parameter p = group.getSpecialization().findByVariable(variable);
if (p != null) {
NodeExecutionData execution = p.getSpecification().getExecution();
if (execution != null) {
LocalVariable var = frameState.getValue(execution);
if (var == null) {
throw new AssertionError();
}
IfTriple triple = createTypeCheckOrCast(frameState, group, new TypeGuard(p.getType(), execution.getIndex()), specializationExecution, true, false);
if (triple != null) {
triples.add(triple);
}
}
}
}
return triples;
}
private ExecutableTypeData createExecuteAndSpecializeType() {
SpecializationData polymorphicSpecialization = node.getPolymorphicSpecialization();
TypeMirror polymorphicType = polymorphicSpecialization.getReturnType().getType();
List<TypeMirror> parameters = new ArrayList<>();
for (Parameter param : polymorphicSpecialization.getSignatureParameters()) {
parameters.add(param.getType());
}
return new ExecutableTypeData(node, polymorphicType, createExecuteAndSpecializeName(), node.getFrameType(), parameters);
}
private List<TypeMirror> resolveOptimizedImplicitSourceTypes(NodeExecutionData execution, TypeMirror targetType) {
List<TypeMirror> allSourceTypes = typeSystem.lookupSourceTypes(targetType);
List<TypeMirror> filteredSourceTypes = new ArrayList<>();
for (TypeMirror sourceType : allSourceTypes) {
ExecutableTypeData executableType = resolveTargetExecutable(execution, sourceType);
if (executableType == null) {
continue;
}
if (!isPrimitive(sourceType) || !boxingEliminationEnabled) {
continue;
}
if (!typeEquals(executableType.getReturnType(), sourceType)) {
continue;
}
filteredSourceTypes.add(sourceType);
}
return filteredSourceTypes;
}
private ChildExecutionResult createExecuteChildImplicitCast(CodeTreeBuilder parent, FrameState originalFrameState, FrameState frameState, NodeExecutionData execution, LocalVariable target) {
CodeTreeBuilder builder = parent.create();
List<TypeMirror> originalSourceTypes = typeSystem.lookupSourceTypes(target.getTypeMirror());
List<TypeMirror> sourceTypes = resolveOptimizedImplicitSourceTypes(execution, target.getTypeMirror());
TypeGuard typeGuard = new TypeGuard(target.getTypeMirror(), execution.getIndex());
boolean throwsUnexpected = false;
boolean elseIf = false;
for (TypeMirror sourceType : sourceTypes) {
ExecutableTypeData executableType = resolveTargetExecutable(execution, sourceType);
elseIf = builder.startIf(elseIf);
throwsUnexpected |= executableType.hasUnexpectedValue();
builder.startGroup();
CodeTree tree = multiState.createContainsOnly(frameState, originalSourceTypes.indexOf(sourceType), 1, new Object[]{typeGuard}, new Object[]{typeGuard});
if (!tree.isEmpty()) {
builder.tree(tree);
builder.string(" && ");
}
builder.tree(multiState.createIsNotAny(frameState, reachableSpecializationsArray));
builder.end();
builder.end();
builder.startBlock();
CodeTree value = callChildExecuteMethod(execution, executableType, frameState);
value = expect(executableType.getReturnType(), sourceType, value);
throwsUnexpected |= needsCastTo(executableType.getReturnType(), sourceType);
ImplicitCastData cast = typeSystem.lookupCast(sourceType, target.getTypeMirror());
if (cast != null) {
String localName = createSourceTypeLocalName(target, sourceType);
builder.startStatement().string(localName).string(" = ").tree(value).end();
value = callMethod(frameState, null, cast.getMethod(), CodeTreeBuilder.singleString(localName));
}
builder.startStatement().string(target.getName()).string(" = ").tree(value).end();
builder.end();
}
if (elseIf) {
builder.startElseBlock();
}
LocalVariable genericValue = target.makeGeneric(context).nextName();
builder.tree(createAssignExecuteChild(originalFrameState, frameState, builder, execution, node.getGenericExecutableType(null), genericValue));
builder.startStatement().string(target.getName()).string(" = ");
CodeTree implicitState = multiState.createExtractInteger(frameState, typeGuard);
builder.tree(TypeSystemCodeGenerator.implicitExpectFlat(typeSystem, target.getTypeMirror(), genericValue.createReference(), implicitState));
builder.end();
if (!sourceTypes.isEmpty()) {
builder.end();
}
return new ChildExecutionResult(builder.build(), throwsUnexpected);
}
private static class ChildExecutionResult {
CodeTree code;
final boolean throwsUnexpectedResult;
ChildExecutionResult(CodeTree code, boolean throwsUnexpectedResult) {
this.code = code;
this.throwsUnexpectedResult = throwsUnexpectedResult;
}
}
private static class ExecuteDelegationResult {
public final CodeTree tree;
public final boolean hasFallthrough;
ExecuteDelegationResult(CodeTree tree, boolean hasFallthrough) {
this.tree = tree;
this.hasFallthrough = hasFallthrough;
}
}
static int getRequiredStateBits(TypeSystemData types, Object object) {
if (object instanceof SpecializationData) {
SpecializationData specialization = (SpecializationData) object;
if (specialization.isPolymorphic()) {
return 0;
} else {
return 1;
}
} else if (object instanceof TypeGuard) {
TypeGuard guard = (TypeGuard) object;
TypeMirror type = guard.getType();
List<TypeMirror> sourceTypes = types.lookupSourceTypes(type);
if (sourceTypes.size() > 1) {
return sourceTypes.size();
}
throw new AssertionError();
} else if (object instanceof GuardExpression) {
return 1;
} else {
throw new AssertionError();
}
}
private static final class MultiStateBitSet extends MultiBitSet<StateBitSet> {
private final List<StateBitSet> all;
MultiStateBitSet(List<StateBitSet> all, List<StateBitSet> active) {
super(active);
this.all = all;
}
int getAllCapacity() {
int length = 0;
for (BitSet a : all) {
length += a.getCapacity();
}
return length;
}
void declareFields(CodeTypeElement clazz) {
for (StateBitSet bitSet : all) {
bitSet.declareFields(clazz);
}
}
void addParametersTo(FrameState frameState, CodeExecutableElement targetMethod) {
for (BitSet set : getSets()) {
LocalVariable local = frameState.get(set.getName());
if (local != null) {
targetMethod.addParameter(local.createParameter());
}
}
}
void removeParametersFrom(CodeExecutableElement targetMethod) {
for (VariableElement var : targetMethod.getParameters().toArray(new VariableElement[0])) {
for (BitSet set : getSets()) {
if (var.getSimpleName().toString().equals(set.getName())) {
targetMethod.getParameters().remove(var);
}
}
}
}
void addReferencesTo(FrameState frameState, CodeTreeBuilder builder) {
for (BitSet set : getSets()) {
LocalVariable local = frameState.get(set.getName());
if (local != null) {
builder.tree(local.createReference());
}
}
}
CodeTree createLoad(FrameState frameState) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
for (BitSet bitSet : getSets()) {
builder.tree(bitSet.createLoad(frameState));
}
return builder.build();
}
CodeTree createLoad(FrameState frameState, List<SpecializationData> specializations) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
for (StateBitSet bitSet : getSets()) {
boolean relevant = false;
for (SpecializationData specialization : specializations) {
if (bitSet.isRelevantForSpecialization(specialization)) {
relevant = true;
break;
}
}
if (relevant) {
builder.tree(bitSet.createLoad(frameState));
}
}
return builder.build();
}
CodeTree createLoadContainsSpecialization(FrameState frameState) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
for (StateBitSet bitSet : getSets()) {
if (!bitSet.containsSpecialization()) {
continue;
}
builder.tree(bitSet.createLoad(frameState));
}
return builder.build();
}
}
private abstract static class NodeBitSet extends BitSet {
private final boolean needsVolatile;
NodeBitSet(String name, Object[] objects, boolean needsVolatile) {
super(name, objects);
this.needsVolatile = needsVolatile;
}
void declareFields(CodeTypeElement clazz) {
CodeVariableElement var = clazz.add(FlatNodeGenFactory.createNodeField(PRIVATE, getType(), getName() + "_", ProcessorContext.getInstance().getTypes().CompilerDirectives_CompilationFinal));
if (needsVolatile) {
var.getModifiers().add(Modifier.VOLATILE);
}
}
String getOldName() {
return "old" + ElementUtils.firstLetterUpperCase(getName());
}
String getNewName() {
return "new" + ElementUtils.firstLetterUpperCase(getName());
}
}
private class StateBitSet extends NodeBitSet {
private final Set<SpecializationData> relevantSpecializations;
StateBitSet(Object[] objects, SpecializationData[] relevantSpecializations, boolean needsVolatile, int index) {
super("state_" + index, objects, needsVolatile);
this.relevantSpecializations = new HashSet<>(Arrays.asList(relevantSpecializations));
}
@Override
protected int calculateRequiredBits(Object object) {
return getRequiredStateBits(typeSystem, object);
}
boolean isRelevantForSpecialization(SpecializationData specialization) {
return relevantSpecializations.contains(specialization);
}
boolean containsSpecialization() {
for (Object o : getObjects()) {
if (o instanceof SpecializationData) {
return true;
}
}
return false;
}
}
private static class ExcludeBitSet extends NodeBitSet {
ExcludeBitSet(SpecializationData[] specializations, boolean needsVolatile) {
super("exclude", specializations, needsVolatile);
}
@Override
protected int calculateRequiredBits(Object object) {
if (object instanceof SpecializationData) {
mayBeExcluded((SpecializationData) object);
SpecializationData specialization = (SpecializationData) object;
if (specialization.isPolymorphic()) {
return 0;
} else if (specialization.isUninitialized()) {
return 0;
}
if (!specialization.getExceptions().isEmpty() || !specialization.getExcludedBy().isEmpty()) {
return 1;
}
return 0;
}
throw new IllegalArgumentException();
}
}
static final class FrameState {
private final FlatNodeGenFactory factory;
private final Map<String, LocalVariable> values = new HashMap<>();
private final Map<String, Boolean> directValues = new HashMap<>();
private final NodeExecutionMode mode;
private final CodeExecutableElement method;
private FrameState(FlatNodeGenFactory factory, NodeExecutionMode mode, CodeExecutableElement method) {
this.factory = factory;
this.mode = mode;
this.method = method;
}
private final List<TypeMirror> caughtTypes = new ArrayList<>();
public void addCaughtException(TypeMirror exceptionType) {
this.caughtTypes.add(exceptionType);
}
public void addThrownExceptions(ExecutableElement calledMethod) {
TruffleTypes types = ProcessorContext.getInstance().getTypes();
outer: for (TypeMirror thrownType : calledMethod.getThrownTypes()) {
if (!ElementUtils.isAssignable(thrownType, ProcessorContext.getInstance().getType(RuntimeException.class))) {
if (factory.generatorMode != GeneratorMode.EXPORTED_MESSAGE && ElementUtils.isAssignable(thrownType, types.UnexpectedResultException)) {
continue outer;
}
for (TypeMirror caughtType : caughtTypes) {
if (ElementUtils.typeEquals(caughtType, thrownType)) {
continue outer;
}
}
boolean found = false;
for (TypeMirror foundType : method.getThrownTypes()) {
if (ElementUtils.typeEquals(thrownType, foundType)) {
found = true;
break;
}
}
if (!found) {
method.getThrownTypes().add(thrownType);
}
}
}
}
public NodeExecutionMode getMode() {
return mode;
}
public void setBoolean(String name, boolean value) {
directValues.put(name, value);
}
public boolean getBoolean(String name, boolean defaultValue) {
Boolean bool = directValues.get(name);
if (bool == null) {
return defaultValue;
} else {
return bool;
}
}
public static FrameState load(FlatNodeGenFactory factory, ExecutableTypeData type, int varargsThreshold, NodeExecutionMode mode, CodeExecutableElement method) {
FrameState context = new FrameState(factory, mode, method);
context.loadEvaluatedValues(type, varargsThreshold);
return context;
}
private void loadEvaluatedValues(ExecutableTypeData executedType, int varargsThreshold) {
TypeMirror frame = executedType.getFrameParameter();
if (frame == null) {
removeValue(FRAME_VALUE);
} else {
set(FRAME_VALUE, new LocalVariable(frame, FRAME_VALUE, null));
}
for (NodeFieldData field : factory.node.getFields()) {
String fieldName = fieldValueName(field);
CodeTree lookupValue;
if (getMode().isUncached()) {
lookupValue = CodeTreeBuilder.createBuilder().defaultValue(field.getType()).build();
} else {
lookupValue = CodeTreeBuilder.createBuilder().string("this.", field.getName()).build();
}
values.put(fieldName, new LocalVariable(field.getType(), fieldName, lookupValue));
}
boolean varargs = needsVarargs(false, varargsThreshold);
List<TypeMirror> evaluatedParameter = executedType.getEvaluatedParameters();
int evaluatedIndex = 0;
for (int executionIndex = 0; executionIndex < factory.node.getExecutionCount(); executionIndex++) {
NodeExecutionData execution = factory.node.getChildExecutions().get(executionIndex);
if (evaluatedIndex < executedType.getEvaluatedCount()) {
TypeMirror evaluatedType = evaluatedParameter.get(evaluatedIndex);
LocalVariable value = createValue(execution, evaluatedType);
if (varargs) {
value = value.accessWith(createReadVarargs(evaluatedIndex));
}
values.put(value.getName(), value.makeOriginal());
evaluatedIndex++;
}
}
}
public static FrameState load(FlatNodeGenFactory factory, NodeExecutionMode mode, CodeExecutableElement method) {
return load(factory, factory.createExecuteAndSpecializeType(), Integer.MAX_VALUE, mode, method);
}
public FrameState copy() {
FrameState copy = new FrameState(factory, mode, method);
copy.values.putAll(values);
copy.caughtTypes.addAll(caughtTypes);
copy.directValues.putAll(directValues);
return copy;
}
private static String fieldValueName(NodeFieldData field) {
return field.getName() + "Value";
}
@SuppressWarnings("static-method")
public LocalVariable createValue(NodeExecutionData execution, TypeMirror type) {
return new LocalVariable(type, valueName(execution), null);
}
private static String valueName(NodeExecutionData execution) {
return execution.getName() + "Value";
}
public void set(String id, LocalVariable var) {
values.put(id, var);
}
public void set(NodeExecutionData execution, LocalVariable var) {
set(valueName(execution), var);
}
public LocalVariable get(String id) {
return values.get(id);
}
public LocalVariable getValue(NodeExecutionData execution) {
return get(valueName(execution));
}
public LocalVariable getValue(int signatureIndex) {
List<NodeExecutionData> childExecutions = factory.node.getChildExecutions();
if (signatureIndex < childExecutions.size()) {
return getValue(childExecutions.get(signatureIndex));
} else {
return null;
}
}
public void removeValue(String id) {
values.remove(id);
}
public void setValue(NodeExecutionData execution, LocalVariable var) {
values.put(valueName(execution), var);
}
private boolean needsVarargs(boolean requireLoaded, int varArgsThreshold) {
int size = 0;
for (NodeExecutionData execution : factory.node.getChildExecutions()) {
if (requireLoaded && getValue(execution) == null) {
continue;
}
size++;
}
return size >= varArgsThreshold;
}
private static CodeTree createReadVarargs(int i) {
return CodeTreeBuilder.createBuilder().string("args_[").string(String.valueOf(i)).string("]").build();
}
public void addReferencesTo(CodeTreeBuilder builder, String... optionalNames) {
for (String var : optionalNames) {
LocalVariable local = values.get(var);
if (local != null) {
builder.tree(local.createReference());
}
}
List<NodeExecutionData> executions = factory.node.getChildExecutions();
for (NodeExecutionData execution : executions) {
LocalVariable var = getValue(execution);
if (var != null) {
builder.startGroup().tree(var.createReference()).end();
}
}
}
public void addParametersTo(CodeExecutableElement targetMethod, int varArgsThreshold, String... optionalNames) {
for (String var : optionalNames) {
LocalVariable local = values.get(var);
if (local != null) {
targetMethod.addParameter(local.createParameter());
}
}
if (needsVarargs(true, varArgsThreshold)) {
targetMethod.addParameter(new CodeVariableElement(factory.getType(Object[].class), "args_"));
targetMethod.setVarArgs(true);
} else {
for (NodeExecutionData execution : factory.node.getChildExecutions()) {
LocalVariable var = getValue(execution);
if (var != null) {
targetMethod.addParameter(var.createParameter());
}
}
}
}
@Override
public String toString() {
return "LocalContext [values=" + values + "]";
}
}
static final class LocalVariable {
private final TypeMirror typeMirror;
private final CodeTree accessorTree;
private final String name;
LocalVariable(TypeMirror typeMirror, String name, CodeTree accessorTree) {
Objects.requireNonNull(typeMirror);
this.typeMirror = typeMirror;
this.accessorTree = accessorTree;
this.name = name;
}
public String getName() {
return name;
}
private static String createNextName(String name) {
return name + "_";
}
public TypeMirror getTypeMirror() {
return typeMirror;
}
public CodeVariableElement createParameter() {
return new CodeVariableElement(getTypeMirror(), getName());
}
public CodeTree createDeclaration(CodeTree init) {
return CodeTreeBuilder.createBuilder().declaration(getTypeMirror(), getName(), init).build();
}
public CodeTree createReference() {
if (accessorTree != null) {
return accessorTree;
} else {
return CodeTreeBuilder.singleString(getName());
}
}
public LocalVariable newType(TypeMirror newType) {
return new LocalVariable(newType, name, accessorTree);
}
public LocalVariable accessWith(CodeTree tree) {
return new LocalVariable(typeMirror, name, tree);
}
public LocalVariable nextName() {
return new LocalVariable(typeMirror, createNextName(name), accessorTree);
}
public LocalVariable makeOriginal() {
return new LocalVariable(typeMirror, name, accessorTree);
}
public LocalVariable makeGeneric(ProcessorContext context) {
return newType(context.getType(Object.class));
}
@Override
public String toString() {
return "Local[type = " + getTypeMirror() + ", name = " + name + ", accessWith = " + accessorTree + "]";
}
}
private static class BoxingSplit {
private final SpecializationGroup group;
private final TypeMirror[] primitiveSignature;
BoxingSplit(SpecializationGroup group, TypeMirror[] primitiveSignature) {
this.group = group;
this.primitiveSignature = primitiveSignature;
}
public String getName() {
StringBuilder b = new StringBuilder();
String sep = "";
for (TypeMirror typeMirror : primitiveSignature) {
b.append(sep).append(firstLetterLowerCase(getSimpleName(typeMirror)));
sep = "_";
}
return b.toString();
}
}
private enum NodeExecutionMode {
FAST_PATH,
SLOW_PATH,
UNCACHED,
FALLBACK_GUARD;
public boolean isGuardFallback() {
return this == FALLBACK_GUARD;
}
public boolean isUncached() {
return this == NodeExecutionMode.UNCACHED;
}
public boolean isSlowPath() {
return this == NodeExecutionMode.SLOW_PATH;
}
public final boolean isFastPath() {
return this == FAST_PATH;
}
}
}