/* * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.oracle.truffle.dsl.processor.generator; import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.createClass; import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.createTransferToInterpreterAndInvalidate; import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.isTypeBoxingOptimized; import static com.oracle.truffle.dsl.processor.java.ElementUtils.compareType; import static com.oracle.truffle.dsl.processor.java.ElementUtils.fromTypeMirror; import static com.oracle.truffle.dsl.processor.java.ElementUtils.getTypeId; import static com.oracle.truffle.dsl.processor.java.ElementUtils.isObject; 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 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.PROTECTED; 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.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Objects; import java.util.Set; 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.TypeMirror; import javax.lang.model.util.ElementFilter; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.UnsupportedSpecializationException; import com.oracle.truffle.api.dsl.internal.SuppressFBWarnings; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.nodes.InvalidAssumptionException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.nodes.Node.Children; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.UnexpectedResultException; import com.oracle.truffle.dsl.processor.ProcessorContext; import com.oracle.truffle.dsl.processor.expression.DSLExpression; 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.CodeVariableElement; import com.oracle.truffle.dsl.processor.java.model.GeneratedTypeMirror; 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.ShortCircuitData; import com.oracle.truffle.dsl.processor.model.SpecializationData; 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; @SuppressWarnings("deprecation") public class DefaultNodeGenFactory { private static final String FRAME_VALUE = TemplateMethod.FRAME_NAME; private static final String NAME_SUFFIX = "_"; private static final String NODE_SUFFIX = "NodeGen"; private final ProcessorContext context; private final NodeData node; private final TypeSystemData typeSystem; private final TypeMirror genericType; private final com.oracle.truffle.api.dsl.internal.DSLOptions options; private final boolean singleSpecializable; private final int varArgsThreshold; private final Set<TypeMirror> expectedTypes = new HashSet<>(); private final Set<NodeExecutionData> usedExecuteChildMethods = new HashSet<>(); private boolean nextUsed; private boolean singleSpecializableUnsupportedUsed; private List<ExecutableTypeData> usedTypes; private List<SpecializationData> reachableSpecializations; public DefaultNodeGenFactory(ProcessorContext context, NodeData node) { this.context = context; this.node = node; this.typeSystem = node.getTypeSystem(); this.genericType = context.getType(Object.class); this.options = typeSystem.getOptions(); this.varArgsThreshold = calculateVarArgsThreshold(); this.reachableSpecializations = calculateReachableSpecializations(); this.singleSpecializable = isSingleSpecializableImpl(); this.usedTypes = filterBaseExecutableTypes(node.getExecutableTypes(), reachableSpecializations); } private int calculateVarArgsThreshold() { TypeMirror specialization = context.getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class); TypeElement specializationType = fromTypeMirror(specialization); int maxParameters = 0; for (ExecutableElement element : ElementFilter.methodsIn(specializationType.getEnclosedElements())) { if (element.getSimpleName().contentEquals("acceptAndExecute")) { maxParameters = Math.max(maxParameters, element.getParameters().size()); } } return maxParameters; } public static String nodeTypeName(NodeData node) { return resolveNodeId(node) + NODE_SUFFIX; } private static String assumptionName(AssumptionExpression assumption) { return assumption.getId() + NAME_SUFFIX; } private static String resolveNodeId(NodeData node) { String nodeid = node.getNodeId(); if (nodeid.endsWith("Node") && !nodeid.equals("Node")) { nodeid = nodeid.substring(0, nodeid.length() - 4); } return nodeid; } public static TypeMirror nodeType(NodeData node) { return new GeneratedTypeMirror(ElementUtils.getPackageName(node.getTemplateType()), nodeTypeName(node)); } private static String specializationTypeName(SpecializationData specialization) { String id; if (specialization == null) { id = "Base"; } else { id = specialization.getId(); } return id + "Node_"; } private TypeMirror specializationType(SpecializationData specialization) { return new GeneratedTypeMirror(ElementUtils.getPackageName(node.getTemplateType()) + "." + nodeTypeName(node), specializationTypeName(specialization)); } private static String polymorphicTypeProfileFieldName(NodeExecutionData execution) { return execution.getName() + "Type" + NAME_SUFFIX; } private static String nodeFieldName(NodeExecutionData execution) { return execution.getName() + NAME_SUFFIX; } private static String specializationStartFieldName() { return "specialization" + NAME_SUFFIX; } private static String excludedFieldName(SpecializationData specialization) { return "exclude" + specialization.getId() + NAME_SUFFIX; } private static String executeChildMethodName(NodeExecutionData execution, TypeMirror type) { return "execute" + ElementUtils.firstLetterUpperCase(execution.getName()) + (ElementUtils.isObject(type) ? "" : getTypeId(type)) + NAME_SUFFIX; } private CodeTree accessParent(String name) { if (singleSpecializable) { if (name == null) { return CodeTreeBuilder.singleString("this"); } else { return CodeTreeBuilder.singleString(name); } } else { if (name == null) { return CodeTreeBuilder.singleString("root"); } else { return CodeTreeBuilder.createBuilder().string("root.").string(name).build(); } } } public CodeTypeElement create(CodeTypeElement clazz) { for (NodeChildData child : node.getChildren()) { clazz.addOptional(createAccessChildMethod(child)); } for (NodeFieldData field : node.getFields()) { if (!field.isGenerated()) { continue; } clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), field.getType(), field.getName())); if (field.getGetter() != null && field.getGetter().getModifiers().contains(Modifier.ABSTRACT)) { CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), field.getGetter()); method.getModifiers().remove(Modifier.ABSTRACT); method.createBuilder().startReturn().string("this.").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) { clazz.add(createNodeField(PRIVATE, execution.getNodeType(), nodeFieldName(execution), Child.class)); } } for (NodeExecutionData execution : node.getChildExecutions()) { if (!resolvePolymorphicExecutables(execution).isEmpty()) { clazz.add(createNodeField(PRIVATE, getType(Class.class), polymorphicTypeProfileFieldName(execution), CompilationFinal.class)); } } for (SpecializationData specialization : node.getSpecializations()) { if (mayBeExcluded(specialization)) { clazz.add(createNodeField(PRIVATE, getType(boolean.class), excludedFieldName(specialization), CompilationFinal.class)); } } clazz.add(createGetCostMethod()); avoidFindbugsProblems(clazz); if (singleSpecializable) { SpecializationData specialization = reachableSpecializations.iterator().next(); for (ExecutableTypeData execType : usedTypes) { if (execType.getMethod() == null) { boolean foundDelegate = false; for (ExecutableTypeData type : usedTypes) { if (type == execType) { continue; } if (findFastPathDelegate(specialization.getReturnType().getType(), type, usedTypes) == execType) { foundDelegate = true; break; } } // just exclude synthetic execute methods that were not delegated to if (!foundDelegate) { continue; } } clazz.add(createExecutableTypeOverride(usedTypes, execType)); } if (singleSpecializableUnsupportedUsed) { addUnsupportedMethod(clazz); } } else { for (ExecutableTypeData execType : usedTypes) { if (execType.getMethod() == null) { continue; } clazz.add(createExecutableTypeOverride(usedTypes, execType)); } clazz.getImplements().add(getType(com.oracle.truffle.api.dsl.internal.SpecializedNode.class)); clazz.add(createMethodGetSpecializationNode()); clazz.add(createDeepCopyMethod()); SpecializationData specializationStart = createSpecializations(clazz); clazz.add(createNodeField(PRIVATE, specializationType(null), specializationStartFieldName(), Child.class)); for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) { CodeTreeBuilder builder = ((CodeExecutableElement) constructor).appendBuilder(); builder.startStatement(); builder.string("this.").string(specializationStartFieldName()); builder.string(" = ").tree(createCallCreateMethod(specializationStart, "this", null)); builder.end(); } } for (TypeMirror type : ElementUtils.uniqueSortedTypes(expectedTypes, false)) { if (!typeSystem.hasType(type)) { clazz.addOptional(TypeSystemCodeGenerator.createExpectMethod(PRIVATE, typeSystem, context.getType(Object.class), type)); } } return clazz; } private void avoidFindbugsProblems(CodeTypeElement clazz) { TypeElement type = context.getEnvironment().getElementUtils().getTypeElement(SuppressFBWarnings.class.getName()); boolean foundComparison = false; outer: for (SpecializationData specialization : node.getSpecializations()) { for (GuardExpression guard : specialization.getGuards()) { if (guard.getExpression().containsComparisons()) { foundComparison = true; break outer; } } } if (foundComparison) { CodeAnnotationMirror annotation = new CodeAnnotationMirror((DeclaredType) type.asType()); annotation.setElementValue(annotation.findExecutableElement("value"), new CodeAnnotationValue("SA_LOCAL_SELF_COMPARISON")); clazz.addAnnotationMirror(annotation); } } private void addUnsupportedMethod(CodeTypeElement clazz) { CodeVariableElement seenUnsupportedField = new CodeVariableElement(modifiers(PRIVATE), getType(boolean.class), "seenUnsupported0"); seenUnsupportedField.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(CompilationFinal.class))); clazz.add(seenUnsupportedField); LocalContext locals = LocalContext.load(this); CodeExecutableElement method = locals.createMethod(modifiers(PRIVATE), getType(UnsupportedSpecializationException.class), "unsupported", varArgsThreshold); CodeTreeBuilder builder = method.createBuilder(); builder.startIf().string("!").string(seenUnsupportedField.getName()).end().startBlock(); builder.startStatement().startStaticCall(getType(CompilerDirectives.class), "transferToInterpreterAndInvalidate").end().end(); builder.startStatement().string(seenUnsupportedField.getName()).string(" = true").end(); builder.end(); builder.startReturn(); builder.startNew(getType(UnsupportedSpecializationException.class)); builder.string("this"); builder.tree(createGetSuppliedChildren()); locals.addReferencesTo(builder); builder.end(); builder.end(); clazz.add(method); } private CodeExecutableElement createNodeConstructor(CodeTypeElement clazz, ExecutableElement superConstructor) { CodeExecutableElement constructor = GeneratorUtils.createConstructorUsingFields(modifiers(), clazz, superConstructor); ElementUtils.setVisibility(constructor.getModifiers(), ElementUtils.getVisibility(superConstructor.getModifiers())); List<CodeVariableElement> childParameters = new ArrayList<>(); for (NodeChildData child : node.getChildren()) { 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()); for (NodeChildData child : node.getChildren()) { 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, 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) { 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.isIndexed()) { accessorBuilder.string("[").string(String.valueOf(execution.getChildIndex())).string("]"); } CodeTree accessor = accessorBuilder.build(); if (createCast != null && execution.getChild().getCardinality().isOne()) { accessor = callMethod(null, createCast.getMethod(), accessor); } if (execution.isIndexed()) { CodeTreeBuilder nullCheck = builder.create(); nullCheck.string(name).string(" != null && ").string(String.valueOf(execution.getChildIndex())).string(" < ").string(name).string(".length").string(" ? "); nullCheck.tree(accessor); nullCheck.string(" : null"); accessor = nullCheck.build(); } builder.tree(accessor); builder.end(); } return constructor; } private static boolean mayBeExcluded(SpecializationData specialization) { return !specialization.getExceptions().isEmpty() || !specialization.getExcludedBy().isEmpty(); } private SpecializationData createSpecializations(CodeTypeElement clazz) { CodeTypeElement baseSpecialization = clazz.add(createBaseSpecialization()); TypeMirror baseSpecializationType = baseSpecialization.asType(); Map<SpecializationData, CodeTypeElement> generated = new LinkedHashMap<>(); List<SpecializationData> generateSpecializations = new ArrayList<>(); generateSpecializations.add(node.getUninitializedSpecialization()); if (needsPolymorphic()) { generateSpecializations.add(node.getPolymorphicSpecialization()); } generateSpecializations.addAll(reachableSpecializations); for (SpecializationData specialization : generateSpecializations) { generated.put(specialization, clazz.add(createSpecialization(specialization, baseSpecializationType))); } baseSpecialization.addOptional(createCreateNext(generated)); baseSpecialization.addOptional(createCreateFallback(generated)); baseSpecialization.addOptional(createCreatePolymorphic(generated)); baseSpecialization.addOptional(createGetNext(baseSpecialization)); for (NodeExecutionData execution : node.getChildExecutions()) { Collection<TypeMirror> specializedTypes = node.findSpecializedTypes(execution); specializedTypes.add(genericType); for (TypeMirror specializedType : specializedTypes) { if (isExecuteChildShared(execution, specializedType)) { baseSpecialization.addOptional(createExecuteChildMethod(execution, specializedType)); } } } return node.getUninitializedSpecialization(); } private boolean needsPolymorphic() { int signatureSize = node.getSignatureSize(); boolean allEvaluated = true; for (ExecutableTypeData type : usedTypes) { if (type.getEvaluatedCount() != signatureSize) { allEvaluated = false; } } if (allEvaluated) { return false; } if (reachableSpecializations.size() != 1) { return true; } SpecializationData specialization = reachableSpecializations.get(0); for (Parameter parameter : specialization.getSignatureParameters()) { TypeMirror type = parameter.getType(); if (type != null && typeSystem.hasImplicitSourceTypes(type)) { return true; } } if (specialization.hasMultipleInstances()) { return true; } return false; } // create specialization private CodeTypeElement createBaseSpecialization() { CodeTypeElement clazz = createClass(node, null, modifiers(PRIVATE, ABSTRACT, STATIC), specializationTypeName(null), typeSystem.getContext().getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class)); clazz.addOptional(createSpecializationConstructor(clazz, null, null)); CodeVariableElement rootField = new CodeVariableElement(modifiers(PROTECTED), nodeType(node), "root"); rootField.addAnnotationMirror(new CodeAnnotationMirror(context.getDeclaredType(CompilationFinal.class))); clazz.add(rootField); clazz.addOptional(createUnsupported()); clazz.add(createSetRootMethod()); clazz.add(createGetSuppliedChildrenMethod()); clazz.add(createAcceptAndExecute()); for (ExecutableTypeData type : usedTypes) { clazz.add(createFastPathExecuteMethod(null, type, usedTypes)); } return clazz; } private Element createSetRootMethod() { CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), context.getType(void.class), "setRoot", new CodeVariableElement(getType(Node.class), "root")); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = method.createBuilder(); builder.startStatement().string("this.root = ").cast(ElementUtils.fillInGenericWildcards(nodeType(node))).string("root").end(); return method; } private Element createAcceptAndExecute() { ExecutableTypeData executableElement = createSpecializationNodeSignature(node.getSignatureSize()); LocalContext currentLocals = LocalContext.load(this, executableElement, varArgsThreshold); CodeExecutableElement executable = createExecuteMethod(null, executableElement, currentLocals, false, varArgsThreshold); executable.getModifiers().add(FINAL); CodeTreeBuilder builder = executable.createBuilder(); CodeTree receiver = CodeTreeBuilder.singleString("this"); builder.tree(createCallDelegateExecute(builder, receiver, currentLocals, executableElement, node.getGenericExecutableType(null))); return executable; } private ExecutableTypeData createSpecializationNodeSignature(int argumentCount) { TypeMirror[] parameters = new TypeMirror[argumentCount]; Arrays.fill(parameters, genericType); return new ExecutableTypeData(node, genericType, "acceptAndExecute", context.getType(Frame.class), Arrays.asList(parameters)); } private boolean shouldImplementExecutableType(SpecializationData specialization, ExecutableTypeData executableType) { // always implement the root execute method. they are declared abstract in the base node. if (executableType.getDelegatedTo() == null) { return true; } // specializations with more parameters are just ignored if (executableType.getEvaluatedCount() > node.getExecutionCount()) { return false; } if (!isSubtypeBoxed(context, specialization.getReturnType().getType(), executableType.getReturnType())) { return false; } // the evaluated signature might be compatible to the specialization boolean specializationCompatible = true; List<TypeMirror> signatureParameters = executableType.getSignatureParameters(); for (int i = 0; i < signatureParameters.size(); i++) { TypeMirror evaluatedType = signatureParameters.get(i); TypeMirror specializedType = specialization.findParameterOrDie(node.getChildExecutions().get(i)).getType(); if (!isSubtypeBoxed(context, evaluatedType, specializedType) && !isSubtypeBoxed(context, specializedType, evaluatedType)) { specializationCompatible = false; break; } } if (!specializationCompatible) { return false; } // possibly trigger void optimization for a specialization if it is enabled if (isVoid(executableType.getReturnType())) { if (isTypeBoxingOptimized(options.voidBoxingOptimization(), specialization.getReturnType().getType())) { return true; } } // trigger type boxing elimination for unevaluated arguments for (int i = executableType.getEvaluatedCount(); i < node.getExecutionCount(); i++) { NodeExecutionData execution = node.getChildExecutions().get(i); TypeMirror specializedType = specialization.findParameterOrDie(execution).getType(); if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), specializedType)) { // it does not make sense to do type boxing elimination for children with // no type specialized execute method if (execution.getChild() != null) { ExecutableTypeData executedType = execution.getChild().findExecutableType(specializedType); if (executedType != null) { return true; } } } } // trigger type boxing elimination for return types if (typeEquals(executableType.getReturnType(), specialization.getReturnType().getType())) { if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), executableType.getReturnType())) { return true; } } // trigger generation for evaluated assignable type matches other than generic for (int i = 0; i < signatureParameters.size(); i++) { TypeMirror evaluatedType = signatureParameters.get(i); NodeExecutionData execution = node.getChildExecutions().get(i); TypeMirror specializedType = specialization.findParameterOrDie(execution).getType(); if (isSubtypeBoxed(context, evaluatedType, specializedType) && !isObject(specializedType)) { return true; } } return false; } private List<ExecutableTypeData> filterBaseExecutableTypes(List<ExecutableTypeData> executableTypes, List<SpecializationData> specializations) { Set<TypeMirror> returnTypes = new HashSet<>(); for (SpecializationData specialization : node.getSpecializations()) { returnTypes.add(specialization.getReturnType().getType()); } List<ExecutableTypeData> prefilteredTypes = new ArrayList<>(); for (ExecutableTypeData type : executableTypes) { if (type.getDelegatedTo() == null || shouldAlwaysImplementExecutableType(type)) { prefilteredTypes.add(type); } else { boolean foundSubtype = false; for (TypeMirror returnType : returnTypes) { if (isSubtypeBoxed(context, returnType, type.getReturnType())) { foundSubtype = true; } } if (foundSubtype) { prefilteredTypes.add(type); } } } Set<ExecutableTypeData> types = new HashSet<>(); type: for (ExecutableTypeData type : prefilteredTypes) { for (SpecializationData specialization : specializations) { if (shouldImplementExecutableType(specialization, type) || shouldAlwaysImplementExecutableType(type)) { types.add(type); continue type; } } } Set<ExecutableTypeData> delegatesToAdd = new HashSet<>(); do { delegatesToAdd.clear(); for (ExecutableTypeData type : types) { ExecutableTypeData delegate = type.getDelegatedTo(); if (delegate != null && !types.contains(delegate)) { delegatesToAdd.add(delegate); } } types.addAll(delegatesToAdd); } while (!delegatesToAdd.isEmpty()); List<ExecutableTypeData> newUsedTypes = new ArrayList<>(types); Collections.sort(newUsedTypes); return newUsedTypes; } private boolean shouldAlwaysImplementExecutableType(ExecutableTypeData type) { return type.isAbstract() || !(type.hasUnexpectedValue(context) && type.getMethod() != null); } private CodeTypeElement createSpecialization(SpecializationData specialization, TypeMirror baseType) { CodeTypeElement clazz = createClass(node, specialization, modifiers(PRIVATE, STATIC, FINAL), specializationTypeName(specialization), baseType); CodeExecutableElement constructor = clazz.addOptional(createSpecializationConstructor(clazz, specialization, null)); for (Parameter p : specialization.getSignatureParameters()) { TypeMirror targetType = p.getType(); if (typeSystem.hasImplicitSourceTypes(targetType)) { NodeExecutionData execution = p.getSpecification().getExecution(); CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, p.getType()); if (implicitProfile != null) { implicitProfile.getModifiers().add(PRIVATE); implicitProfile.getModifiers().add(FINAL); clazz.add(implicitProfile); } } } if (specialization.isFallback()) { clazz.add(createFallbackGuardMethod()); } clazz.addOptional(createSpecializationCreateMethod(specialization, constructor)); clazz.addOptional(createMergeMethod(specialization)); clazz.addOptional(createIsSameMethod(specialization)); clazz.addOptional(createIsIdenticalMethod(specialization)); // get types that should get implemented List<ExecutableTypeData> types = new ArrayList<>(); for (ExecutableTypeData type : node.getExecutableTypes()) { if (shouldImplementExecutableType(specialization, type)) { types.add(type); } } for (ExecutableTypeData type : types) { clazz.add(createFastPathExecuteMethod(specialization, type, types)); } return clazz; } public static List<Parameter> getDynamicParameters(TemplateMethod method) { List<Parameter> parameters = new ArrayList<>(); for (Parameter param : method.getReturnTypeAndParameters()) { if (param.getSpecification().isLocal()) { // ignore parameters passed by locals continue; } else if (param.getVariableElement() != null && param.getVariableElement().getAnnotation(Cached.class) != null) { // ignore cached parameters continue; } parameters.add(param); } return parameters; } private Element createDeepCopyMethod() { if (singleSpecializable) { return null; } CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), getType(Node.class), "deepCopy"); executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = executable.createBuilder(); builder.startReturn().startStaticCall(getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class), "updateRoot").string("super.deepCopy()").end().end(); return executable; } private Element createGetCostMethod() { TypeMirror returnType = getType(NodeCost.class); CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getCost"); executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = executable.createBuilder(); if (singleSpecializable) { builder.startReturn().staticReference(getType(NodeCost.class), "MONOMORPHIC").end().end(); } else { builder.startReturn().startCall(specializationStartFieldName(), "getNodeCost").end().end(); } return executable; } private Element createIsIdenticalMethod(SpecializationData specialization) { boolean cacheBoundGuard = specialization.hasMultipleInstances(); if (!cacheBoundGuard) { return null; } LocalContext currentLocals = LocalContext.load(this, createSpecializationNodeSignature(node.getSignatureSize()), varArgsThreshold); currentLocals.loadFastPathState(specialization); CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getType(boolean.class), "isIdentical"); method.addParameter(new CodeVariableElement(getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class), "other")); currentLocals.addParametersTo(method, varArgsThreshold, FRAME_VALUE); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); final CodeTreeBuilder builder = method.createBuilder(); SpecializationGroup group = SpecializationGroup.create(specialization); SpecializationBody executionFactory = new SpecializationBody(true, false) { @Override public CodeTree createBody(SpecializationData s, LocalContext values) { return builder.create().returnTrue().build(); } }; builder.tree(createGuardAndCast(builder, group, null, currentLocals, executionFactory)); builder.returnFalse(); return method; } private CodeExecutableElement createIsSameMethod(SpecializationData specialization) { if (!specialization.isSpecialized() || !options.implicitCastOptimization().isDuplicateTail()) { return null; } List<CodeVariableElement> profiles = new ArrayList<>(); for (Parameter parameter : specialization.getSignatureParameters()) { NodeExecutionData execution = parameter.getSpecification().getExecution(); if (execution == null) { continue; } CodeVariableElement var = createImplicitProfileParameter(execution, parameter.getType()); if (var != null) { profiles.add(var); } } if (profiles.isEmpty()) { return null; } CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getType(boolean.class), "isSame"); method.addParameter(new CodeVariableElement(getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class), "other")); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = method.createBuilder(); builder.startReturn(); builder.string("super.isSame(other)"); for (CodeVariableElement profile : profiles) { builder.string(" && "); builder.string("this.").string(profile.getName()).string(" == ").string("(").cast(specializationType(specialization)).string("other).").string(profile.getName()); } builder.end(); return method; } private Element createMergeMethod(SpecializationData specialization) { if (specialization.getExcludedBy().isEmpty() && !specialization.isPolymorphic()) { return null; } TypeMirror specializationNodeType = getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class); LocalContext currentLocals = LocalContext.load(this, createSpecializationNodeSignature(node.getSignatureSize()), varArgsThreshold); CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), specializationNodeType, "merge"); executable.addParameter(new CodeVariableElement(specializationNodeType, "newNode")); currentLocals.addParametersTo(executable, varArgsThreshold, FRAME_VALUE); executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = executable.createBuilder(); if (specialization.isPolymorphic()) { builder.startReturn(); builder.startCall("polymorphicMerge"); builder.string("newNode"); builder.startCall("super", "merge"); builder.string("newNode"); currentLocals.addReferencesTo(builder, FRAME_VALUE); builder.end(); builder.end(); builder.end(); } else { boolean elseIf = false; for (SpecializationData containedSpecialization : specialization.getExcludedBy()) { elseIf = builder.startIf(elseIf); builder.string("newNode.getClass() == ").typeLiteral(specializationType(containedSpecialization)); builder.end(); builder.startBlock(); builder.statement("removeSame(\"Contained by " + containedSpecialization.createReferenceName() + "\")"); builder.end(); } builder.startReturn(); builder.startCall("super", "merge"); builder.string("newNode"); currentLocals.addReferencesTo(builder, FRAME_VALUE); builder.end(); builder.end(); } return executable; } private Element createCreateFallback(Map<SpecializationData, CodeTypeElement> generatedSpecializationClasses) { SpecializationData fallback = node.getGenericSpecialization(); if (fallback == null) { return null; } CodeTypeElement generatedType = generatedSpecializationClasses.get(fallback); if (generatedType == null) { return null; } TypeMirror returnType = getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class); CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), returnType, "createFallback"); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); method.createBuilder().startReturn().tree(createCallCreateMethod(fallback, null, null)).end(); return method; } private Element createCreatePolymorphic(Map<SpecializationData, CodeTypeElement> generatedSpecializationClasses) { SpecializationData polymorphic = node.getPolymorphicSpecialization(); CodeTypeElement generatedPolymorphic = generatedSpecializationClasses.get(polymorphic); if (generatedPolymorphic == null) { return null; } TypeMirror returnType = getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class); CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), returnType, "createPolymorphic"); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); method.createBuilder().startReturn().tree(createCallCreateMethod(polymorphic, null, null)).end(); return method; } private CodeExecutableElement createCreateNext(final Map<SpecializationData, CodeTypeElement> specializationClasses) { final LocalContext locals = LocalContext.load(this); CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class), "createNext", varArgsThreshold, FRAME_VALUE); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = method.createBuilder(); SpecializationGroup group = createSpecializationGroups(); CodeTree execution = createGuardAndCast(builder, group, null, locals, new SpecializationBody(false, false) { @Override public CodeTree createBody(SpecializationData specialization, LocalContext values) { CodeTypeElement generatedType = specializationClasses.get(specialization); if (generatedType == null) { throw new AssertionError("No generated type for " + specialization); } return createSlowPathExecute(specialization, values); } }); builder.tree(execution); if (hasFallthrough(group, genericType, locals, false, null)) { builder.returnNull(); } return method; } private CodeExecutableElement createFallbackGuardMethod() { boolean frameUsed = node.isFrameUsedByAnyGuard(); LocalContext locals = LocalContext.load(this); if (!frameUsed) { locals.removeValue(FRAME_VALUE); } CodeExecutableElement boundaryMethod = locals.createMethod(modifiers(PRIVATE), getType(boolean.class), "guardFallback", varArgsThreshold, FRAME_VALUE); if (!frameUsed) { boundaryMethod.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(TruffleBoundary.class))); } CodeTreeBuilder builder = boundaryMethod.createBuilder(); builder.startReturn(); builder.startCall("createNext"); locals.addReferencesTo(builder, FRAME_VALUE); builder.end(); builder.string(" == null"); builder.end(); return boundaryMethod; } private ExecutableElement createAccessChildMethod(NodeChildData child) { if (child.getAccessElement() != null && child.getAccessElement().getModifiers().contains(Modifier.ABSTRACT)) { ExecutableElement getter = (ExecutableElement) child.getAccessElement(); CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), 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 (child.getCardinality().isMany()) { builder.startReturn().startNewArray((ArrayType) child.getOriginalType(), null); for (NodeExecutionData execution : executions) { builder.string(nodeFieldName(execution)); } builder.end().end(); } else { for (NodeExecutionData execution : executions) { builder.startReturn().string("this.").string(nodeFieldName(execution)).end(); break; } } return method; } return null; } private Element createUnsupported() { SpecializationData fallback = node.getGenericSpecialization(); if (fallback == null || optimizeFallback(fallback) || fallback.getMethod() == null) { return null; } LocalContext locals = LocalContext.load(this); CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), genericType, "unsupported", varArgsThreshold, FRAME_VALUE); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = method.createBuilder(); builder.startReturn(); builder.tree(callTemplateMethod(accessParent(null), fallback, locals)); builder.end(); return method; } private boolean isSingleSpecializableImpl() { if (reachableSpecializations.size() != 1) { return false; } SpecializationData specialization = reachableSpecializations.get(0); for (Parameter parameter : specialization.getSignatureParameters()) { TypeMirror type = parameter.getType(); if (type != null && typeSystem.hasImplicitSourceTypes(type)) { return false; } } if (!specialization.getAssumptionExpressions().isEmpty()) { return false; } if (specialization.getCaches().size() > 0) { // TODO chumer: caches do not yet support single specialization. // it could be worthwhile to explore if this is possible return false; } return true; } private List<SpecializationData> calculateReachableSpecializations() { List<SpecializationData> specializations = new ArrayList<>(); for (SpecializationData specialization : node.getSpecializations()) { if (specialization.isReachable() && // (specialization.isSpecialized() // || (specialization.isFallback() && optimizeFallback(specialization)))) { specializations.add(specialization); } } return specializations; } private boolean optimizeFallback(SpecializationData specialization) { switch (options.optimizeFallback()) { case NEVER: return false; case DECLARED: return specialization.getMethod() != null; case ALWAYS: return true; default: throw new AssertionError(); } } private CodeExecutableElement createExecutableTypeOverride(List<ExecutableTypeData> usedExecutables, ExecutableTypeData execType) { LocalContext locals = LocalContext.load(this, execType, Integer.MAX_VALUE); CodeExecutableElement method = createExecuteMethod(null, execType, locals, true, Integer.MAX_VALUE); CodeTreeBuilder builder = method.createBuilder(); if (singleSpecializable) { SpecializationData specialization = reachableSpecializations.iterator().next(); builder.tree(createFastPath(builder, specialization, execType, usedExecutables, locals)); } else { // create acceptAndExecute ExecutableTypeData delegate = execType; CodeTree receiver = CodeTreeBuilder.singleString(specializationStartFieldName()); builder.tree(createCallDelegateExecute(builder, receiver, locals, execType, delegate)); } return method; } private Element createMethodGetSpecializationNode() { TypeMirror returntype = getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class); CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returntype, "getSpecializationNode"); method.createBuilder().startReturn().string(specializationStartFieldName()).end(); return method; } private TypeMirror getType(Class<?> clazz) { return context.getType(clazz); } private CodeVariableElement createNodeField(Modifier visibility, TypeMirror type, String name, Class<?> annotationType) { CodeVariableElement childField = new CodeVariableElement(modifiers(), type, name); childField.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(annotationType))); setVisibility(childField.getModifiers(), visibility); return childField; } private static List<ExecutableTypeData> resolveSpecializedExecutables(NodeExecutionData execution, Collection<TypeMirror> types, com.oracle.truffle.api.dsl.internal.DSLOptions.TypeBoxingOptimization optimization) { if (optimization == com.oracle.truffle.api.dsl.internal.DSLOptions.TypeBoxingOptimization.NONE) { return Collections.emptyList(); } else if (types.isEmpty()) { return Collections.emptyList(); } List<ExecutableTypeData> executables = new ArrayList<>(); for (TypeMirror type : types) { if (!isTypeBoxingOptimized(optimization, type)) { continue; } if (execution.getChild() == null) { continue; } ExecutableTypeData foundType = execution.getChild().getNodeData().findExecutableType(type, execution.getChild().getExecuteWith().size()); if (foundType != null) { executables.add(foundType); } } return executables; } private static CodeTree callMethod(CodeTree receiver, ExecutableElement method, CodeTree... boundValues) { CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); if (method.getModifiers().contains(STATIC)) { builder.startStaticCall(method.getEnclosingElement().asType(), method.getSimpleName().toString()); } else { builder.startCall(receiver, method.getSimpleName().toString()); } int index = -1; for (VariableElement parameter : method.getParameters()) { index++; if (index < boundValues.length) { CodeTree tree = boundValues[index]; if (tree != null) { builder.tree(tree); continue; } } builder.defaultValue(parameter.asType()); } builder.end(); return builder.build(); } private CodeTree[] bindExecuteMethodParameters(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) { List<NodeExecutionData> executeWith = execution != null ? execution.getChild().getExecuteWith() : null; List<CodeTree> values = new ArrayList<>(); if (method.getFrameParameter() != null) { LocalVariable frameLocal = currentValues.get(FRAME_VALUE); if (frameLocal == null) { values.add(CodeTreeBuilder.singleString("null")); } else { values.add(createTypeSafeReference(frameLocal, method.getFrameParameter())); } } 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 (parameterExecution.isShortCircuit()) { if (evaluatedIndex < method.getEvaluatedCount()) { TypeMirror targetType = method.getEvaluatedParameters().get(evaluatedIndex); LocalVariable shortCircuit = currentValues.getShortCircuit(parameterExecution); if (shortCircuit != null) { values.add(createTypeSafeReference(shortCircuit, targetType)); } else { values.add(CodeTreeBuilder.createBuilder().defaultValue(targetType).build()); } evaluatedIndex++; } } if (evaluatedIndex < method.getEvaluatedCount()) { TypeMirror targetType = method.getEvaluatedParameters().get(evaluatedIndex); LocalVariable value = currentValues.getValue(parameterExecution); if (value != null) { values.add(createTypeSafeReference(value, targetType)); } else { values.add(CodeTreeBuilder.createBuilder().defaultValue(targetType).build()); } evaluatedIndex++; } } return values.toArray(new CodeTree[values.size()]); } private CodeTree callExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) { CodeTree receiver = execution != null ? accessParent(nodeFieldName(execution)) : null; return callMethod(receiver, method.getMethod(), bindExecuteMethodParameters(execution, method, currentValues)); } private CodeTree callTemplateMethod(CodeTree receiver, TemplateMethod method, LocalContext currentValues) { CodeTree[] bindings = new CodeTree[method.getParameters().size()]; int signatureIndex = 0; for (int i = 0; i < bindings.length; i++) { Parameter parameter = method.getParameters().get(i); LocalVariable var = currentValues.get(parameter, signatureIndex); if (var == null) { var = currentValues.get(parameter.getLocalName()); } if (var != null) { bindings[i] = createTypeSafeReference(var, parameter.getType()); } if (parameter.getSpecification().isSignature()) { signatureIndex++; } } return callMethod(receiver, method.getMethod(), bindings); } private CodeTree createTypeSafeReference(LocalVariable var, TypeMirror targetType) { CodeTree valueReference = var.createReference(); TypeMirror sourceType = var.getTypeMirror(); if (targetType == null || sourceType == null) { return valueReference; } if (needsCastTo(sourceType, targetType)) { valueReference = TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference); } return valueReference; } private SpecializationGroup createSpecializationGroups() { return SpecializationGroup.createDefault(reachableSpecializations); } private CodeTree createSlowPathExecute(SpecializationData specialization, LocalContext currentValues) { CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); if (specialization.isFallback()) { return builder.returnNull().build(); } if (node.isFrameUsedByAnyGuard()) { builder.tree(createTransferToInterpreterAndInvalidate()); } // caches unbound to guards are invoked after all guards for (CacheExpression cache : specialization.getCaches()) { if (!specialization.isCacheBoundByGuard(cache)) { initializeCache(builder, specialization, cache, currentValues); } } boolean hasAssumptions = !specialization.getAssumptionExpressions().isEmpty(); if (hasAssumptions) { for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) { CodeTree assumptions = DSLExpressionGenerator.write(assumption.getExpression(), accessParent(null), castBoundTypes(bindExpressionValues(assumption.getExpression(), specialization, currentValues))); String name = assumptionName(assumption); // needs specialization index for assumption to make unique String varName = name + specialization.getIndex(); TypeMirror type = assumption.getExpression().getResolvedType(); builder.declaration(type, varName, assumptions); currentValues.set(name, new LocalVariable(type, varName, null, null)); } builder.startIf(); String sep = ""; for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) { LocalVariable assumptionVar = currentValues.get(assumptionName(assumption)); if (assumptionVar == null) { throw new AssertionError("assumption var not resolved"); } builder.string(sep); builder.startCall("isValid").tree(assumptionVar.createReference()).end(); sep = " && "; } builder.end(); builder.startBlock(); } for (SpecializationData otherSpeciailzation : node.getSpecializations()) { if (otherSpeciailzation == specialization) { continue; } if (otherSpeciailzation.getExcludedBy().contains(specialization)) { builder.startStatement(); builder.tree(accessParent(excludedFieldName(otherSpeciailzation))); builder.string(" = true"); builder.end(); } } CodeTree create = createCallCreateMethod(specialization, null, currentValues); if (specialization.hasMultipleInstances()) { builder.declaration(getType(com.oracle.truffle.api.dsl.internal.SpecializationNode.class), "s", create); DSLExpression limitExpression = specialization.getLimitExpression(); CodeTree limitExpressionTree; if (limitExpression == null) { limitExpressionTree = CodeTreeBuilder.singleString("3"); } else { limitExpressionTree = DSLExpressionGenerator.write(limitExpression, accessParent(null), // castBoundTypes(bindExpressionValues(limitExpression, specialization, currentValues))); } builder.startIf().string("countSame(s) < ").tree(limitExpressionTree).end().startBlock(); builder.statement("return s"); builder.end(); } else { builder.startReturn().tree(create).end(); } if (hasAssumptions) { builder.end(); } if (mayBeExcluded(specialization)) { CodeTreeBuilder checkHasSeenBuilder = builder.create(); checkHasSeenBuilder.startIf().string("!").tree(accessParent(excludedFieldName(specialization))).end().startBlock(); checkHasSeenBuilder.tree(builder.build()); checkHasSeenBuilder.end(); return checkHasSeenBuilder.build(); } return builder.build(); } private boolean hasFallthrough(SpecializationGroup group, TypeMirror forType, LocalContext currentValues, boolean fastPath, List<GuardExpression> ignoreGuards) { for (TypeGuard guard : group.getTypeGuards()) { if (currentValues.getValue(guard.getSignatureIndex()) == null) { // not evaluated return true; } LocalVariable value = currentValues.getValue(guard.getSignatureIndex()); if (needsCastTo(value.getTypeMirror(), guard.getType())) { return true; } } List<GuardExpression> guards = new ArrayList<>(group.getGuards()); List<GuardExpression> elseConnectable = group.findElseConnectableGuards(); guards.removeAll(elseConnectable); if (ignoreGuards != null) { guards.removeAll(ignoreGuards); } SpecializationData specialization = group.getSpecialization(); if (specialization != null && fastPath) { for (ListIterator<GuardExpression> iterator = guards.listIterator(); iterator.hasNext();) { GuardExpression guard = iterator.next(); if (!specialization.isDynamicParameterBound(guard.getExpression())) { iterator.remove(); } } } if (!guards.isEmpty()) { return true; } if (!fastPath && specialization != null && !specialization.getAssumptionExpressions().isEmpty()) { return true; } if (!fastPath && specialization != null && mayBeExcluded(specialization)) { return true; } if (!elseConnectable.isEmpty()) { SpecializationGroup previous = group.getPrevious(); if (previous != null && hasFallthrough(previous, forType, currentValues, fastPath, previous.getGuards())) { return true; } } List<SpecializationGroup> groupChildren = group.getChildren(); if (!groupChildren.isEmpty()) { return hasFallthrough(groupChildren.get(groupChildren.size() - 1), forType, currentValues, fastPath, ignoreGuards); } return false; } private Element createGetNext(CodeTypeElement type) { if (!nextUsed) { return null; } CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), type.asType(), "getNext"); CodeTreeBuilder builder = method.createBuilder(); builder.startReturn().cast(type.asType(), CodeTreeBuilder.singleString("this.next")).end(); return method; } private Element createGetSuppliedChildrenMethod() { ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class)); CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), nodeArray, "getSuppliedChildren"); method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); CodeTreeBuilder builder = method.createBuilder(); builder.startReturn().tree(createGetSuppliedChildren()).end(); return method; } private CodeTree createGetSuppliedChildren() { ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class)); CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); builder.startNewArray(nodeArray, null); for (int i = 0; i < node.getChildExecutions().size(); i++) { NodeExecutionData execution = node.getChildExecutions().get(i); if (execution.isShortCircuit()) { builder.nullLiteral(); } if (execution.getChild() == null) { builder.nullLiteral(); } else { builder.tree(accessParent(nodeFieldName(execution))); } } builder.end(); return builder.build(); } // create specialization private CodeTree createCallCreateMethod(SpecializationData specialization, String rootName, LocalContext currentValues) { CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); TypeMirror specializationType = specializationType(specialization); if (useLazyClassLoading()) { builder.startStaticCall(specializationType(specialization), "create"); } else { builder.startNew(specializationType); } if (rootName != null) { builder.string(rootName); } else { builder.string("root"); } if (currentValues != null) { for (Parameter p : specialization.getSignatureParameters()) { CodeVariableElement var = createImplicitProfileParameter(p.getSpecification().getExecution(), p.getType()); if (var != null) { LocalVariable variable = currentValues.get(p.getLocalName()); if (variable == null) { throw new AssertionError("Could not bind cached value " + p.getLocalName() + ": " + currentValues); } builder.tree(variable.original().createReference()); } } for (CacheExpression cache : specialization.getCaches()) { LocalVariable variable = currentValues.get(cache.getParameter().getLocalName()); if (variable == null) { throw new AssertionError("Could not bind cached value " + cache.getParameter().getLocalName() + ": " + currentValues); } builder.tree(variable.createReference()); } for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) { LocalVariable variable = currentValues.get(assumptionName(assumption)); if (variable == null) { throw new AssertionError("Could not bind assumption value " + assumption.getId() + ": " + currentValues); } builder.tree(variable.createReference()); } } builder.end(); return builder.build(); } private Element createSpecializationCreateMethod(SpecializationData specialization, CodeExecutableElement constructor) { if (!useLazyClassLoading()) { return null; } CodeExecutableElement executable = CodeExecutableElement.clone(context.getEnvironment(), constructor); executable.setReturnType(specializationType(null)); executable.setSimpleName(CodeNames.of("create")); executable.getModifiers().add(STATIC); CodeTreeBuilder builder = executable.createBuilder(); builder.startReturn().startNew(specializationType(specialization)); for (VariableElement parameter : executable.getParameters()) { builder.string(parameter.getSimpleName().toString()); } builder.end().end(); return executable; } private boolean useLazyClassLoading() { return options.useLazyClassLoading() && !singleSpecializable; } private static String implicitClassFieldName(NodeExecutionData execution) { return execution.getName() + "ImplicitType"; } private static String implicitNodeFieldName(NodeExecutionData execution) { return execution.getName() + "Cast"; } private CodeExecutableElement createSpecializationConstructor(CodeTypeElement clazz, SpecializationData specialization, String constantIndex) { CodeExecutableElement constructor = new CodeExecutableElement(modifiers(), null, clazz.getSimpleName().toString()); constructor.addParameter(new CodeVariableElement(nodeType(node), "root")); CodeTreeBuilder builder = constructor.createBuilder(); if (specialization == null) { if (constantIndex == null) { builder.statement("super(index)"); constructor.addParameter(new CodeVariableElement(getType(int.class), "index")); } else { builder.startStatement().startSuperCall().string(constantIndex).end().end(); } builder.statement("this.root = root"); } else { int index = resolveSpecializationIndex(specialization); builder.startStatement().startSuperCall().string("root").string(String.valueOf(index)).end().end(); for (Parameter p : specialization.getSignatureParameters()) { NodeExecutionData execution = p.getSpecification().getExecution(); CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, p.getType()); if (implicitProfile != null) { LocalVariable var = LocalVariable.fromParameter(p).makeGeneric(context); String implicitFieldName = implicitProfile.getName(); if (options.implicitCastOptimization().isDuplicateTail()) { constructor.addParameter(var.createParameter()); CodeTree implicitType = TypeSystemCodeGenerator.implicitType(typeSystem, p.getType(), var.createReference()); builder.startStatement().string("this.").string(implicitFieldName).string(" = ").tree(implicitType).end(); } else if (options.implicitCastOptimization().isMergeCasts()) { // use node that supports polymorphism constructor.addParameter(var.createParameter()); builder.startStatement().string("this.").string(implicitFieldName).string(" = ").tree(ImplicitCastNodeFactory.create(typeSystem, p.getType(), var.createReference())).end(); } else { throw new AssertionError(); } } } for (CacheExpression cache : specialization.getCaches()) { String name = cache.getParameter().getLocalName(); TypeMirror type = cache.getParameter().getType(); if (ElementUtils.isAssignable(type, new ArrayCodeTypeMirror(getType(Node.class)))) { CodeVariableElement var = clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), type, name)); var.addAnnotationMirror(new CodeAnnotationMirror(context.getDeclaredType(Children.class))); } else if (ElementUtils.isAssignable(type, getType(Node.class))) { CodeVariableElement var = clazz.add(new CodeVariableElement(modifiers(PRIVATE), type, name)); var.addAnnotationMirror(new CodeAnnotationMirror(context.getDeclaredType(Child.class))); } else { clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), type, name)); } constructor.addParameter(new CodeVariableElement(type, name)); builder.startStatement().string("this.").string(name).string(" = ").string(name).end(); } for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) { String name = assumptionName(assumption); TypeMirror type = assumption.getExpression().getResolvedType(); CodeVariableElement field = clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), type, name)); field.addAnnotationMirror(new CodeAnnotationMirror(context.getDeclaredType(CompilationFinal.class))); constructor.addParameter(new CodeVariableElement(type, name)); builder.startStatement().string("this.").string(name).string(" = ").string(name).end(); } } if (constructor.getParameters().isEmpty()) { // do not generate default constructor return null; } return constructor; } // TODO this logic can be inlined to the parser as soon as the old NodeGen layout is gone private static int resolveSpecializationIndex(SpecializationData specialization) { if (specialization.isFallback()) { return Integer.MAX_VALUE - 1; } else if (specialization.isUninitialized()) { return Integer.MAX_VALUE; } else if (specialization.isPolymorphic()) { return 0; } else { return specialization.getIndex(); } } private CodeTree createThrowUnsupported(LocalContext currentValues) { singleSpecializableUnsupportedUsed = true; CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); builder.startThrow().startCall("unsupported"); currentValues.addReferencesTo(builder); builder.end().end(); return builder.build(); } private CodeTree createCallNext(CodeTreeBuilder parent, ExecutableTypeData currentType, ExecutableTypeData callType, LocalContext currentValues) { if (singleSpecializable) { return createThrowUnsupported(currentValues); } CodeTreeBuilder callBuilder = parent.create(); callBuilder.tree(createCallDelegateExecute(callBuilder, CodeTreeBuilder.singleString("getNext()"), currentValues, currentType, callType)); nextUsed = true; return callBuilder.build(); } private CodeTree createCallRemove(String reason, ExecutableTypeData forType, LocalContext currentValues) { if (singleSpecializable) { return createThrowUnsupported(currentValues); } CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); builder.startCall("remove"); builder.doubleQuote(reason); currentValues.addReferencesTo(builder, FRAME_VALUE); builder.end(); CodeTree call = builder.build(); builder = builder.create(); builder.startReturn(); builder.tree(expectOrCast(genericType, forType, call)); builder.end(); return builder.build(); } private CodeTree createCallDelegate(CodeTreeBuilder parent, String methodName, String reason, ExecutableTypeData forType, LocalContext currentValues) { CodeTreeBuilder builder = parent.create(); builder.startCall(methodName); if (reason != null) { builder.doubleQuote(reason); } currentValues.addReferencesTo(builder, FRAME_VALUE); builder.end(); CodeTree expectOrCast = expectOrCast(genericType, forType, builder.build()); return expectOrCast; } 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 (ElementUtils.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 || ElementUtils.needsCastTo(sourceType, forType)) { expectedTypes.add(forType); return TypeSystemCodeGenerator.expect(typeSystem, forType, tree); } return tree; } private Set<ExecutableTypeData> findSpecializedExecutableTypes(NodeExecutionData execution, TypeMirror type) { if (execution.getChild() == null) { return Collections.emptySet(); } ExecutableTypeData executableType = resolveExecutableType(execution.getChild(), type); Set<ExecutableTypeData> executedTypes = new HashSet<>(); executedTypes.add(executableType); if (typeSystem.hasImplicitSourceTypes(type)) { executedTypes.addAll(resolveSpecializedExecutables(execution, typeSystem.lookupSourceTypes(type), options.implicitTypeBoxingOptimization())); } return executedTypes; } private ExecutableTypeData resolveExecutableType(NodeChildData child, TypeMirror type) { int executeWithCount = child.getExecuteWith().size(); ExecutableTypeData executableType = child.getNodeData().findExecutableType(type, executeWithCount); if (executableType == null) { executableType = child.getNodeData().findAnyGenericExecutableType(context, executeWithCount); } return executableType; } private boolean hasChildUnexpectedResult(NodeExecutionData execution, TypeMirror type) { for (ExecutableTypeData executableType : findSpecializedExecutableTypes(execution, type)) { if (executableType != null && (executableType.hasUnexpectedValue(context) || needsCastTo(executableType.getReturnType(), type))) { return true; } } return false; } private Element createFastPathExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, List<ExecutableTypeData> allTypes) { LocalContext currentLocals = LocalContext.load(this, executedType, Integer.MAX_VALUE); CodeExecutableElement executable = createExecuteMethod(specialization, executedType, currentLocals, false, Integer.MAX_VALUE); CodeTreeBuilder builder = executable.createBuilder(); if (specialization == null) { if (executedType.getDelegatedTo() == null) { executable.getModifiers().add(ABSTRACT); } } else { executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); } builder.tree(createFastPath(builder, specialization, executedType, allTypes, currentLocals)); return executable; } private static final String VARARGS_NAME = "args"; private CodeExecutableElement createExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, LocalContext currentLocals, boolean originalOverride, int varArgs) { TypeMirror returnType = executedType.getReturnType(); if (specialization != null) { currentLocals.loadFastPathState(specialization); } String methodName; if (originalOverride && executedType.getMethod() != null) { methodName = executedType.getMethod().getSimpleName().toString(); } else { methodName = executedType.getUniqueName(); } CodeExecutableElement executable; if (originalOverride && executedType.getMethod() != null) { executable = CodeExecutableElement.clone(context.getEnvironment(), executedType.getMethod()); executable.getAnnotationMirrors().clear(); executable.getModifiers().remove(ABSTRACT); for (VariableElement var : executable.getParameters()) { ((CodeVariableElement) var).getAnnotationMirrors().clear(); } if (executedType.getFrameParameter() != null) { ((CodeVariableElement) executable.getParameters().get(0)).setName(FRAME_VALUE); } if (executable.isVarArgs()) { ((CodeVariableElement) executable.getParameters().get(executable.getParameters().size() - 1)).setName(VARARGS_NAME); } renameOriginalParameters(executedType, executable, currentLocals); } else { executable = currentLocals.createMethod(modifiers(PUBLIC), returnType, methodName, varArgs, FRAME_VALUE); } executable.getThrownTypes().clear(); if (needsUnexpectedResultException(executedType)) { executable.getThrownTypes().add(context.getDeclaredType(UnexpectedResultException.class)); } return executable; } private void renameOriginalParameters(ExecutableTypeData executedType, CodeExecutableElement executable, LocalContext currentLocals) { // rename varargs parameter int evaluatedIndex = 0; for (int executionIndex = 0; executionIndex < node.getExecutionCount(); executionIndex++) { NodeExecutionData execution = node.getChildExecutions().get(executionIndex); if (execution.isShortCircuit()) { if (evaluatedIndex < executedType.getEvaluatedCount()) { TypeMirror evaluatedType = executedType.getEvaluatedParameters().get(evaluatedIndex); LocalVariable shortCircuit = currentLocals.getShortCircuit(execution); if (shortCircuit != null) { currentLocals.setShortCircuitValue(execution, renameExecutableTypeParameter(executable, executedType, evaluatedIndex, evaluatedType, shortCircuit)); } evaluatedIndex++; } } if (evaluatedIndex < executedType.getEvaluatedCount()) { TypeMirror evaluatedType = executedType.getEvaluatedParameters().get(evaluatedIndex); LocalVariable value = currentLocals.getValue(execution); if (value != null) { currentLocals.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(context)) { return false; } SpecializationData polymorphicSpecialization = node.getPolymorphicSpecialization(); if (polymorphicSpecialization != null && isSubtypeBoxed(context, polymorphicSpecialization.getReturnType().getType(), executedType.getReturnType())) { return false; } else { return true; } } private CodeTree createFastPath(CodeTreeBuilder parent, SpecializationData specialization, final ExecutableTypeData executableType, List<ExecutableTypeData> allTypes, LocalContext currentLocals) { final CodeTreeBuilder builder = parent.create(); TypeMirror returnType = executableType.getReturnType(); ExecutableTypeData delegate = null; if (specialization == null) { delegate = executableType.getDelegatedTo(); } if (delegate == null) { delegate = findFastPathDelegate((specialization != null ? specialization.getReturnType().getType() : genericType), executableType, allTypes); } int delegateSignatureCount = delegate != null ? delegate.getSignatureParameters().size() : 0; for (NodeExecutionData execution : node.getChildExecutions()) { if (specialization == null && delegate != null && execution.getIndex() >= delegateSignatureCount) { // we just evaluate children for the next delegate continue; } else if (specialization != null && delegate != null) { // skip if already delegated break; } LocalVariable var = currentLocals.getValue(execution); if (var == null) { TypeMirror targetType; if (specialization == null) { targetType = node.getGenericType(execution); } else { targetType = specialization.findParameterOrDie(execution).getType(); } LocalVariable shortCircuit = resolveShortCircuit(specialization, execution, currentLocals); var = currentLocals.createValue(execution, targetType).nextName(); builder.tree(createAssignExecuteChild(builder, execution, executableType, var, shortCircuit, currentLocals)); currentLocals.setValue(execution, var); } } LocalContext originalValues = currentLocals.copy(); if (delegate != null) { builder.tree(createCallDelegateExecute(builder, null, currentLocals, executableType, delegate)); } else if (specialization == null) { // nothing to do. abstract anyway } else if (specialization.isPolymorphic()) { builder.tree(createCallNext(builder, executableType, node.getGenericExecutableType(executableType), currentLocals)); } else if (specialization.isUninitialized()) { builder.startReturn().tree(createCallDelegate(builder, "uninitialized", null, executableType, currentLocals)).end(); } else { SpecializationGroup group = SpecializationGroup.create(specialization); SpecializationBody executionFactory = new SpecializationBody(true, true) { @Override public CodeTree createBody(SpecializationData s, LocalContext values) { return createFastPathExecute(builder, executableType, s, values); } }; builder.tree(createGuardAndCast(builder, group, executableType, currentLocals, executionFactory)); if (hasFallthrough(group, returnType, originalValues, true, null) || group.getSpecialization().isFallback()) { builder.tree(createCallNext(builder, executableType, node.getGenericExecutableType(executableType), originalValues)); } } return builder.build(); } private CodeTree createCallDelegateExecute(final CodeTreeBuilder parent, CodeTree receiver, LocalContext currentLocals, ExecutableTypeData source, ExecutableTypeData delegate) { CodeTreeBuilder callBuilder = parent.create(); if (singleSpecializable && delegate.getMethod() != null) { callBuilder.startCall(receiver, delegate.getMethod().getSimpleName().toString()); } else { callBuilder.startCall(receiver, delegate.getUniqueName()); } callBuilder.trees(bindExecuteMethodParameters(null, delegate, currentLocals)); callBuilder.end(); CodeTree call = expectOrCast(delegate.getReturnType(), source, callBuilder.build()); CodeTreeBuilder returnBuilder = parent.create(); if (isVoid(source.getReturnType())) { returnBuilder.statement(call); returnBuilder.returnStatement(); } else if (isVoid(delegate.getReturnType())) { returnBuilder.statement(call); returnBuilder.returnDefault(); } else { returnBuilder.startReturn().tree(call).end(); } CodeTreeBuilder builder = parent.create(); if (!needsUnexpectedResultException(source) && needsUnexpectedResultException(delegate)) { builder.startTryBlock(); builder.tree(returnBuilder.build()); builder.end().startCatchBlock(context.getType(UnexpectedResultException.class), "ex"); if (!isVoid(source.getReturnType())) { builder.startReturn().tree(cast(context.getType(Object.class), source.getReturnType(), CodeTreeBuilder.singleString("ex.getResult()"))).end(); } builder.end(); } else { builder.tree(returnBuilder.build()); } return builder.build(); } private ExecutableTypeData findFastPathDelegate(TypeMirror targetType, ExecutableTypeData executableType, List<ExecutableTypeData> allTypes) { if (typeEquals(executableType.getReturnType(), targetType)) { // type matches look for even better delegates for (ExecutableTypeData type : allTypes) { if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) { if (type != executableType) { return type; } } } return null; } else { for (ExecutableTypeData type : allTypes) { if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) { return type; } } int executableIndex = allTypes.indexOf(executableType); int compareIndex = 0; for (ExecutableTypeData type : allTypes) { if (executableIndex != compareIndex && executableType.sameParameters(type)) { int result = ExecutableTypeData.compareType(context, type.getReturnType(), executableType.getReturnType()); if (result < 0) { return type; } else if (result == 0 && executableIndex < compareIndex) { return type; } } compareIndex++; } return null; } } private LocalVariable resolveShortCircuit(SpecializationData specialization, NodeExecutionData execution, LocalContext currentLocals) { LocalVariable shortCircuit = null; if (execution.isShortCircuit()) { shortCircuit = currentLocals.getShortCircuit(execution); if (shortCircuit == null) { SpecializationData resolvedSpecialization = specialization; if (specialization == null) { resolvedSpecialization = node.getGenericSpecialization(); } ShortCircuitData shortCircuitData = resolvedSpecialization.getShortCircuits().get(calculateShortCircuitIndex(execution)); CodeTree access = callTemplateMethod(accessParent(null), shortCircuitData, currentLocals); shortCircuit = currentLocals.createShortCircuitValue(execution).accessWith(access); } else { CodeTree access = shortCircuit.createReference(); shortCircuit = shortCircuit.nextName().accessWith(access); } } return shortCircuit; } private int calculateShortCircuitIndex(NodeExecutionData execution) { int shortCircuitIndex = 0; for (NodeExecutionData otherExectuion : node.getChildExecutions()) { if (otherExectuion.isShortCircuit()) { if (otherExectuion == execution) { break; } shortCircuitIndex++; } } return shortCircuitIndex; } private CodeTree createFastPathExecute(CodeTreeBuilder parent, final ExecutableTypeData forType, SpecializationData specialization, LocalContext currentValues) { CodeTreeBuilder builder = parent.create(); int ifCount = 0; if (specialization.isFallback()) { builder.startIf().startCall("guardFallback"); if (node.isFrameUsedByAnyGuard()) { if (currentValues.get(FRAME_VALUE) != null) { builder.string(FRAME_VALUE); } else { builder.nullLiteral(); } } currentValues.addReferencesTo(builder); builder.end(); builder.end(); builder.startBlock(); ifCount++; } CodeTreeBuilder execute = builder.create(); if (specialization.getMethod() == null) { execute.startReturn(); execute.startCall("unsupported"); currentValues.addReferencesTo(execute, FRAME_VALUE); execute.end(); execute.end(); } else { boolean doReturn = !isVoid(specialization.getMethod().getReturnType()); if (doReturn) { execute.startReturn(); } else { execute.startStatement(); } execute.tree(callTemplateMethod(accessParent(null), specialization, currentValues)); execute.end(); if (!doReturn) { if (isVoid(forType.getReturnType())) { execute.returnStatement(); } else { execute.startReturn(); execute.defaultValue(forType.getReturnType()); execute.end(); } } } builder.tree(createFastPathTryCatchRewriteException(specialization, forType, currentValues, execute.build())); builder.end(ifCount); return builder.build(); } private CodeTree createGuardAndCast(CodeTreeBuilder parent, SpecializationGroup group, ExecutableTypeData forType, LocalContext currentValues, SpecializationBody execution) { CodeTreeBuilder builder = parent.create(); Set<TypeGuard> castGuards; if (execution.needsCastedValues()) { castGuards = null; // cast all } else { castGuards = new HashSet<>(); for (TypeGuard castGuard : group.getTypeGuards()) { if (isTypeGuardUsedInAnyGuardOrCacheBelow(group, currentValues, castGuard, execution.isFastPath())) { castGuards.add(castGuard); } } } SpecializationData specialization = group.getSpecialization(); CodeTree assumptionChecks = null; if (specialization != null && execution.isFastPath() && forType != null) { if (!specialization.getAssumptionExpressions().isEmpty()) { assumptionChecks = createFastPathAssumptionCheck(builder, specialization, forType, currentValues); } } CodeTree[] checkAndCast = createTypeCheckAndLocals(specialization, group.getTypeGuards(), castGuards, currentValues, execution); CodeTree check = checkAndCast[0]; CodeTree cast = checkAndCast[1]; List<GuardExpression> elseGuardExpressions = group.findElseConnectableGuards(); List<GuardExpression> guardExpressions = new ArrayList<>(group.getGuards()); guardExpressions.removeAll(elseGuardExpressions); CodeTree[] methodGuardAndAssertions = createMethodGuardCheck(guardExpressions, specialization, currentValues, execution.isFastPath()); CodeTree methodGuards = methodGuardAndAssertions[0]; CodeTree guardAssertions = methodGuardAndAssertions[1]; if (assumptionChecks != null) { builder.tree(assumptionChecks); } int ifCount = 0; if (!check.isEmpty()) { builder.startIf(); builder.tree(check).end(); builder.startBlock(); ifCount++; } if (!cast.isEmpty()) { builder.tree(cast); } boolean elseIf = !elseGuardExpressions.isEmpty(); if (!methodGuards.isEmpty()) { builder.startIf(elseIf); builder.tree(methodGuards).end(); builder.startBlock(); ifCount++; } else if (elseIf) { builder.startElseBlock(); ifCount++; } if (!guardAssertions.isEmpty()) { builder.tree(guardAssertions); } boolean reachable = isReachableGroup(group, ifCount); if (reachable) { for (SpecializationGroup child : group.getChildren()) { builder.tree(createGuardAndCast(builder, child, forType, currentValues.copy(), execution)); } if (specialization != null) { builder.tree(execution.createBody(specialization, currentValues)); } } builder.end(ifCount); return builder.build(); } private CodeTree createFastPathAssumptionCheck(CodeTreeBuilder parent, SpecializationData specialization, ExecutableTypeData forType, LocalContext currentValues) throws AssertionError { CodeTreeBuilder builder = parent.create(); builder.startTryBlock(); for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) { LocalVariable assumptionVar = currentValues.get(assumptionName(assumption)); if (assumptionVar == null) { throw new AssertionError("Could not resolve assumption var " + currentValues); } builder.startStatement().startCall("check").tree(assumptionVar.createReference()).end().end(); } builder.end().startCatchBlock(getType(InvalidAssumptionException.class), "ae"); builder.startReturn(); List<String> assumptionIds = new ArrayList<>(); for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) { assumptionIds.add(assumption.getId()); } builder.tree(createCallDelegate(builder, "removeThis", String.format("Assumption %s invalidated", assumptionIds), forType, currentValues)); builder.end(); builder.end(); return builder.build(); } private static boolean isReachableGroup(SpecializationGroup group, int ifCount) { if (ifCount != 0) { return true; } SpecializationGroup previous = group.getPreviousGroup(); if (previous == null || previous.findElseConnectableGuards().isEmpty()) { return true; } /* * Hacky else case. In this case the specialization is not reachable due to previous else * branch. This is only true if the minimum state is not checked. */ if (previous.getGuards().size() == 1 && previous.getTypeGuards().isEmpty() && (previous.getParent() == null || previous.getMaxSpecializationIndex() != previous.getParent().getMaxSpecializationIndex())) { return false; } return true; } private boolean isTypeGuardUsedInAnyGuardOrCacheBelow(SpecializationGroup group, LocalContext currentValues, TypeGuard typeGuard, boolean fastPath) { String localName = currentValues.getValue(typeGuard.getSignatureIndex()).getName(); SpecializationData specialization = group.getSpecialization(); for (GuardExpression guard : group.getGuards()) { if (isVariableBoundIn(specialization, guard.getExpression(), localName, currentValues)) { return true; } } if (!fastPath && specialization != null) { for (CacheExpression cache : specialization.getCaches()) { if (isVariableBoundIn(specialization, cache.getExpression(), localName, currentValues)) { return true; } } } for (SpecializationGroup child : group.getChildren()) { if (isTypeGuardUsedInAnyGuardOrCacheBelow(child, currentValues, typeGuard, fastPath)) { return true; } } return false; } private static boolean isVariableBoundIn(SpecializationData specialization, DSLExpression expression, String localName, LocalContext currentValues) throws AssertionError { Map<Variable, LocalVariable> boundValues = bindExpressionValues(expression, specialization, currentValues); for (Variable var : expression.findBoundVariables()) { LocalVariable target = boundValues.get(var); if (target != null && localName.equals(target.getName())) { return true; } } return false; } private CodeExecutableElement createExecuteChildMethod(NodeExecutionData execution, TypeMirror targetType) { if (!usedExecuteChildMethods.contains(execution)) { return null; } LocalContext locals = LocalContext.load(this, createSpecializationNodeSignature(0), Integer.MAX_VALUE); CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), targetType, executeChildMethodName(execution, targetType), Integer.MAX_VALUE, FRAME_VALUE); if (hasChildUnexpectedResult(execution, targetType)) { method.getThrownTypes().add(getType(UnexpectedResultException.class)); } CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, targetType); if (implicitProfile != null) { method.addParameter(implicitProfile); } for (int i = 0; i < execution.getChild().getExecuteWith().size(); i++) { NodeExecutionData executeWith = node.getChildExecutions().get(i); LocalVariable var = locals.createValue(executeWith, genericType); method.addParameter(var.createParameter()); locals.setValue(executeWith, var); } CodeTreeBuilder builder = method.createBuilder(); CodeTree executeChild = createExecuteChild(execution, locals.createValue(execution, targetType), locals, true); if (executeChild.isSingleLine()) { builder.statement(executeChild); } else { builder.tree(executeChild); } return method; } private CodeVariableElement createImplicitProfileParameter(NodeExecutionData execution, TypeMirror targetType) { if (typeSystem.hasImplicitSourceTypes(targetType)) { if (typeEquals(node.getGenericType(execution), targetType)) { return null; } switch (options.implicitCastOptimization()) { case NONE: return null; case DUPLICATE_TAIL: return new CodeVariableElement(getType(Class.class), implicitClassFieldName(execution)); case MERGE_CASTS: return new CodeVariableElement(ImplicitCastNodeFactory.type(typeSystem, targetType), implicitNodeFieldName(execution)); } } return null; } private boolean isExecuteChildShared(NodeExecutionData execution, TypeMirror targetType) { if (isVoid(targetType)) { return false; } else if (isObject(targetType)) { return resolvePolymorphicExecutables(execution).size() >= 1; } else { if (!isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), targetType)) { return false; } if (!typeSystem.hasImplicitSourceTypes(targetType)) { return false; } int uses = 0; for (SpecializationData specialization : node.getSpecializations()) { List<Parameter> parameters = specialization.findByExecutionData(execution); for (Parameter parameter : parameters) { if (targetType.equals(parameter.getType())) { uses++; } } } if (uses > 1) { return resolveSpecializedExecutables(execution, typeSystem.lookupSourceTypes(targetType), options.implicitTypeBoxingOptimization()).size() > 1; } else { return false; } } } private CodeTree createAssignExecuteChild(CodeTreeBuilder parent, NodeExecutionData execution, ExecutableTypeData type, LocalVariable targetValue, LocalVariable shortCircuit, LocalContext currentValues) { CodeTreeBuilder builder = parent.create(); boolean hasUnexpected = hasChildUnexpectedResult(execution, targetValue.getTypeMirror()); CodeTree executeChild; if (isExecuteChildShared(execution, targetValue.getTypeMirror())) { executeChild = createCallSharedExecuteChild(execution, targetValue, currentValues); } else { executeChild = createExecuteChild(execution, targetValue, currentValues, false); } builder.tree(createTryExecuteChild(targetValue, executeChild, shortCircuit == null, hasUnexpected)); if (shortCircuit != null) { currentValues.setShortCircuitValue(execution, shortCircuit.accessWith(null)); } builder.end(); if (hasUnexpected) { builder.startCatchBlock(getType(UnexpectedResultException.class), "ex"); LocalContext slowPathValues = currentValues.copy(); slowPathValues.setValue(execution, targetValue.makeGeneric(context).accessWith(CodeTreeBuilder.singleString("ex.getResult()"))); ExecutableTypeData delegateType = node.getGenericExecutableType(type); boolean found = false; for (NodeExecutionData otherExecution : node.getChildExecutions()) { if (found) { LocalVariable childEvaluatedValue = slowPathValues.createValue(otherExecution, genericType); LocalVariable genericShortCircuit = resolveShortCircuit(null, otherExecution, slowPathValues); builder.tree(createAssignExecuteChild(builder, otherExecution, delegateType, childEvaluatedValue, genericShortCircuit, slowPathValues)); slowPathValues.setValue(otherExecution, childEvaluatedValue); } else { // skip forward already evaluated found = execution == otherExecution; } } builder.tree(createCallNext(builder, type, delegateType, slowPathValues)); builder.end(); } return createShortCircuit(targetValue, shortCircuit, builder.build()); } private static CodeTree createShortCircuit(LocalVariable targetValue, LocalVariable shortCircuitValue, CodeTree tryExecute) { if (shortCircuitValue == null) { return tryExecute; } CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); builder.tree(shortCircuitValue.createDeclaration(shortCircuitValue.createReference())); builder.tree(targetValue.createDeclaration(builder.create().defaultValue(targetValue.getTypeMirror()).build())); builder.startIf().string(shortCircuitValue.getName()).end().startBlock(); builder.tree(tryExecute); 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 CodeTree createCallSharedExecuteChild(NodeExecutionData execution, LocalVariable targetValue, LocalContext currentValues) { if (!isExecuteChildShared(execution, targetValue.getTypeMirror())) { throw new AssertionError("Execute child not shared with method but called."); } usedExecuteChildMethods.add(execution); CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); builder.tree(targetValue.createReference()).string(" = "); builder.startCall(executeChildMethodName(execution, targetValue.getTypeMirror())); if (currentValues.get(FRAME_VALUE) == null) { builder.nullLiteral(); } else { builder.string(FRAME_VALUE); } CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, targetValue.getTypeMirror()); if (implicitProfile != null) { builder.string(implicitProfile.getName()); } for (int i = 0; i < execution.getChild().getExecuteWith().size(); i++) { builder.tree(currentValues.getValue(i).createReference()); } builder.end(); return builder.build(); } private CodeTree createExecuteChild(NodeExecutionData execution, LocalVariable target, LocalContext currentValues, boolean shared) { final CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); CodeTree assignment = createAssignmentStart(target, shared); final Set<ExecutableTypeData> executableTypes = findSpecializedExecutableTypes(execution, target.getTypeMirror()); if (executableTypes.isEmpty()) { throw new AssertionError(); // cannot execute child } else if (executableTypes.size() == 1 && !typeSystem.hasImplicitSourceTypes(target.getTypeMirror())) { ExecutableTypeData executableType = executableTypes.iterator().next(); if (isObject(target.getTypeMirror()) && executableType.getEvaluatedCount() == 0) { return createPolymorphicExecuteChild(execution, target, currentValues, shared); } else { builder.tree(assignment); builder.tree(createSingleExecute(execution, target, currentValues, executableType)); } } else { if (options.implicitCastOptimization().isNone()) { throw new AssertionError("findSpecializedExecutableTypes is always 1 if implicit cast opt is disabled"); } else if (options.implicitCastOptimization().isDuplicateTail()) { builder.tree(createExecuteChildDuplicateTail(builder, execution, assignment, target, currentValues)); } else if (options.implicitCastOptimization().isMergeCasts()) { // TODO throw new UnsupportedOperationException(); } else { throw new AssertionError(); } } return builder.build(); } private CodeTree createSingleExecute(NodeExecutionData execution, LocalVariable target, LocalContext currentValues, ExecutableTypeData executableType) { CodeTree execute = callExecuteMethod(execution, executableType, currentValues); return expect(executableType.getReturnType(), target.getTypeMirror(), execute); } private CodeTree createPolymorphicExecuteChild(NodeExecutionData execution, LocalVariable target, LocalContext currentValues, boolean shared) throws AssertionError { ExecutableTypeData genericExecutableType = execution.getChild().getNodeData().findAnyGenericExecutableType(context, execution.getChild().getExecuteWith().size()); if (genericExecutableType == null) { throw new AssertionError("At least one generic executable method must be available."); } List<ExecutableTypeData> specializedExecutables = resolvePolymorphicExecutables(execution); Collections.sort(specializedExecutables, new Comparator<ExecutableTypeData>() { public int compare(ExecutableTypeData o1, ExecutableTypeData o2) { return compareType(o1.getReturnType(), o2.getReturnType()); } }); CodeTree assignment = createAssignmentStart(target, shared); CodeTree executeGeneric = createSingleExecute(execution, target, currentValues, genericExecutableType); CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); if (specializedExecutables.isEmpty()) { builder.tree(assignment); builder.tree(executeGeneric); } else { final CodeTreeBuilder polyChainBuilder = builder.create(); final String profileField = polymorphicTypeProfileFieldName(execution); final String valueFieldName = "_value"; final String typeFieldName = "_type"; builder.declaration(getType(Class.class), profileField, accessParent(profileField)); boolean encounteredUnexpectedResult = false; boolean hasSpecializedTypes = false; for (ExecutableTypeData executableType : specializedExecutables) { hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes); polyChainBuilder.string(profileField); polyChainBuilder.string(" == ").typeLiteral(executableType.getReturnType()); polyChainBuilder.end(); polyChainBuilder.startBlock(); polyChainBuilder.startStatement(); polyChainBuilder.tree(assignment); polyChainBuilder.tree(createSingleExecute(execution, target, currentValues, executableType)).end(); polyChainBuilder.end(); encounteredUnexpectedResult |= executableType.hasUnexpectedValue(context); } // else if null -> specialize polyChainBuilder.startElseIf().string(profileField).string(" == null").end(); polyChainBuilder.startBlock(); polyChainBuilder.tree(createTransferToInterpreterAndInvalidate()); polyChainBuilder.declaration(context.getType(Class.class), typeFieldName, polyChainBuilder.create().typeLiteral(genericType).build()); polyChainBuilder.startTryBlock(); polyChainBuilder.declaration(genericExecutableType.getReturnType(), valueFieldName, executeGeneric); hasSpecializedTypes = false; for (ExecutableTypeData executableType : specializedExecutables) { hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes); polyChainBuilder.tree(TypeSystemCodeGenerator.check(typeSystem, executableType.getReturnType(), CodeTreeBuilder.singleString(valueFieldName))); polyChainBuilder.end(); polyChainBuilder.startBlock(); polyChainBuilder.startStatement().string(typeFieldName).string(" = ").typeLiteral(executableType.getReturnType()).end(); polyChainBuilder.end(); } polyChainBuilder.startElseBlock(); polyChainBuilder.startStatement().string(typeFieldName).string(" = ").typeLiteral(genericType).end(); polyChainBuilder.end(); polyChainBuilder.startReturn().string(valueFieldName).end(); polyChainBuilder.end().startFinallyBlock(); polyChainBuilder.startStatement().tree(accessParent(profileField)).string(" = ").string(typeFieldName).end(); polyChainBuilder.end(); polyChainBuilder.end(); // else -> execute generic polyChainBuilder.startElseBlock(); polyChainBuilder.startStatement().tree(assignment).tree(executeGeneric).end(); polyChainBuilder.end(); CodeTree executePolymorphic = polyChainBuilder.build(); if (encounteredUnexpectedResult) { builder.startTryBlock(); builder.tree(executePolymorphic); builder.end(); builder.startCatchBlock(getType(UnexpectedResultException.class), "ex"); builder.startStatement().tree(accessParent(profileField)).string(" = ").typeLiteral(genericType).end(); builder.startReturn().string("ex.getResult()").end(); builder.end(); } else { builder.tree(executePolymorphic); } } return builder.build(); } private List<ExecutableTypeData> resolvePolymorphicExecutables(NodeExecutionData execution) { if (singleSpecializable) { return Collections.emptyList(); } Set<TypeMirror> specializedTypes = new HashSet<>(); for (TypeMirror type : node.findSpecializedTypes(execution)) { specializedTypes.addAll(typeSystem.lookupSourceTypes(type)); } return resolveSpecializedExecutables(execution, specializedTypes, options.polymorphicTypeBoxingElimination()); } private static CodeTree createAssignmentStart(LocalVariable target, boolean shared) { CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); if (shared) { builder.string("return "); } else { builder.string(target.getName()).string(" = "); } return builder.build(); } private CodeTree createExecuteChildDuplicateTail(CodeTreeBuilder parent, NodeExecutionData execution, CodeTree assignment, LocalVariable target, LocalContext currentValues) { CodeTreeBuilder builder = parent.create(); List<TypeMirror> sourceTypes = typeSystem.lookupSourceTypes(target.getTypeMirror()); String implicitClassFieldName = implicitClassFieldName(execution); List<ExecutableTypeData> executableTypes = resolveSpecializedExecutables(execution, sourceTypes, options.implicitTypeBoxingOptimization()); boolean elseIf = false; for (ExecutableTypeData executableType : executableTypes) { elseIf = builder.startIf(elseIf); builder.string(implicitClassFieldName).string(" == ").typeLiteral(executableType.getReturnType()); builder.end(); builder.startBlock(); builder.startStatement().tree(assignment); CodeTree execute = callExecuteMethod(execution, executableType, currentValues); ImplicitCastData cast = typeSystem.lookupCast(executableType.getReturnType(), target.getTypeMirror()); if (cast != null) { execute = callMethod(null, cast.getMethod(), execute); } builder.tree(execute); builder.end(); builder.end(); } if (!executableTypes.isEmpty()) { builder.startElseBlock(); } LocalVariable genericValue = target.makeGeneric(context).nextName(); builder.tree(createAssignExecuteChild(builder, execution, node.getGenericExecutableType(null), genericValue, null, currentValues)); if (executableTypes.size() == sourceTypes.size()) { builder.startThrow().startNew(getType(UnexpectedResultException.class)).tree(genericValue.createReference()).end().end(); } else { builder.startStatement().tree(assignment); builder.tree(TypeSystemCodeGenerator.implicitExpect(typeSystem, target.getTypeMirror(), genericValue.createReference(), implicitClassFieldName)); builder.end(); } if (!executableTypes.isEmpty()) { builder.end(); } return builder.build(); } private CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, ExecutableTypeData forType, LocalContext currentValues, CodeTree execution) { if (specialization.getExceptions().isEmpty()) { return execution; } CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); builder.startTryBlock(); builder.tree(execution); TypeMirror[] exceptionTypes = new TypeMirror[specialization.getExceptions().size()]; for (int i = 0; i < exceptionTypes.length; i++) { exceptionTypes[i] = specialization.getExceptions().get(i).getJavaClass(); } builder.end().startCatchBlock(exceptionTypes, "ex"); builder.startStatement().tree(accessParent(excludedFieldName(specialization))).string(" = true").end(); builder.tree(createCallRemove("threw rewrite exception", forType, currentValues)); builder.end(); return builder.build(); } private CodeTree[] createMethodGuardCheck(List<GuardExpression> guardExpressions, SpecializationData specialization, LocalContext currentValues, boolean fastPath) { CodeTreeBuilder expressionBuilder = CodeTreeBuilder.createBuilder(); CodeTreeBuilder assertionBuilder = CodeTreeBuilder.createBuilder(); String and = ""; for (GuardExpression guard : guardExpressions) { DSLExpression expression = guard.getExpression(); Map<Variable, CodeTree> resolvedBindings = castBoundTypes(bindExpressionValues(expression, specialization, currentValues)); CodeTree expressionCode = DSLExpressionGenerator.write(expression, accessParent(null), resolvedBindings); if (!specialization.isDynamicParameterBound(expression) && fastPath) { /* * Guards where no dynamic parameters are bound can just be executed on the fast * path. */ assertionBuilder.startAssert().tree(expressionCode).end(); } else { expressionBuilder.string(and); expressionBuilder.tree(expressionCode); and = " && "; } } return new CodeTree[]{expressionBuilder.build(), assertionBuilder.build()}; } 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 (!ElementUtils.isAssignable(sourceType, targetType)) { resolved = CodeTreeBuilder.createBuilder().cast(targetType, resolved).build(); } resolvedBindings.put(variable, resolved); } return resolvedBindings; } private static Map<Variable, LocalVariable> bindExpressionValues(DSLExpression expression, SpecializationData specialization, LocalContext currentValues) 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."); } // resolve bindings for local context for (Variable variable : boundVariables) { Parameter resolvedParameter = specialization.findByVariable(variable.getResolvedVariable()); if (resolvedParameter != null) { LocalVariable localVariable; if (resolvedParameter.getSpecification().isSignature()) { NodeExecutionData execution = resolvedParameter.getSpecification().getExecution(); localVariable = currentValues.getValue(execution); } else { localVariable = currentValues.get(resolvedParameter.getLocalName()); } if (localVariable != null) { bindings.put(variable, localVariable); } } } return bindings; } private CodeTree[] createTypeCheckAndLocals(SpecializationData specialization, List<TypeGuard> typeGuards, Set<TypeGuard> castGuards, LocalContext currentValues, SpecializationBody specializationExecution) { CodeTreeBuilder checksBuilder = CodeTreeBuilder.createBuilder(); CodeTreeBuilder localsBuilder = CodeTreeBuilder.createBuilder(); for (TypeGuard typeGuard : typeGuards) { int signatureIndex = typeGuard.getSignatureIndex(); LocalVariable value = currentValues.getValue(signatureIndex); TypeMirror targetType = typeGuard.getType(); if (!ElementUtils.needsCastTo(value.getTypeMirror(), targetType)) { continue; } NodeExecutionData execution = node.getChildExecutions().get(signatureIndex); if (!checksBuilder.isEmpty()) { checksBuilder.string(" && "); } CodeTreeBuilder checkBuilder = checksBuilder.create(); CodeTreeBuilder castBuilder = checksBuilder.create(); LocalVariable shortCircuit = currentValues.getShortCircuit(execution); if (shortCircuit != null) { checkBuilder.string("("); CodeTreeBuilder referenceBuilder = checkBuilder.create(); if (!ElementUtils.isPrimitive(shortCircuit.getTypeMirror())) { referenceBuilder.string("(boolean) "); } referenceBuilder.tree(shortCircuit.createReference()); checkBuilder.string("!").tree(referenceBuilder.build()); checkBuilder.string(" || "); castBuilder.tree(referenceBuilder.build()).string(" ? "); } List<ImplicitCastData> sourceTypes = typeSystem.lookupByTargetType(targetType); CodeTree valueReference = value.createReference(); if (sourceTypes.isEmpty()) { checkBuilder.tree(TypeSystemCodeGenerator.check(typeSystem, targetType, value.createReference())); castBuilder.tree(TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference)); } else { com.oracle.truffle.api.dsl.internal.DSLOptions.ImplicitCastOptimization opt = options.implicitCastOptimization(); if (specializationExecution.isFastPath() && !opt.isNone()) { if (opt.isDuplicateTail()) { String typeHintField = implicitClassFieldName(execution); checkBuilder.tree(TypeSystemCodeGenerator.implicitCheck(typeSystem, targetType, valueReference, typeHintField)); castBuilder.tree(TypeSystemCodeGenerator.implicitCast(typeSystem, targetType, valueReference, typeHintField)); } else if (opt.isMergeCasts()) { checkBuilder.tree(ImplicitCastNodeFactory.check(implicitNodeFieldName(execution), valueReference)); castBuilder.tree(ImplicitCastNodeFactory.cast(implicitNodeFieldName(execution), valueReference)); } else { throw new AssertionError("implicit cast opt"); } } else { checkBuilder.tree(TypeSystemCodeGenerator.implicitCheck(typeSystem, targetType, valueReference, null)); castBuilder.tree(TypeSystemCodeGenerator.implicitCast(typeSystem, targetType, valueReference, null)); } } if (shortCircuit != null) { checkBuilder.string(")"); castBuilder.string(" : ").defaultValue(targetType); } if (castGuards == null || castGuards.contains(typeGuard)) { LocalVariable castVariable = currentValues.getValue(execution).nextName().newType(typeGuard.getType()).accessWith(null); currentValues.setValue(execution, castVariable); localsBuilder.tree(castVariable.createDeclaration(castBuilder.build())); } checksBuilder.tree(checkBuilder.build()); } if (specialization != null && !specializationExecution.isFastPath()) { for (CacheExpression cache : specialization.getCaches()) { if (specialization.isCacheBoundByGuard(cache)) { initializeCache(localsBuilder, specialization, cache, currentValues); } } } return new CodeTree[]{checksBuilder.build(), localsBuilder.build()}; } private void initializeCache(CodeTreeBuilder builder, SpecializationData specialization, CacheExpression cache, LocalContext currentValues) { CodeTree initializer = DSLExpressionGenerator.write(cache.getExpression(), accessParent(null), castBoundTypes(bindExpressionValues(cache.getExpression(), specialization, currentValues))); String name = cache.getParameter().getLocalName(); // multiple specializations might use the same name String varName = name + specialization.getIndex(); TypeMirror type = cache.getParameter().getType(); builder.declaration(type, varName, initializer); currentValues.set(name, new LocalVariable(type, varName, null, null)); } public static final class LocalContext { private final DefaultNodeGenFactory factory; private final Map<String, LocalVariable> values = new HashMap<>(); private LocalContext(DefaultNodeGenFactory factory) { this.factory = factory; } public void loadFastPathState(SpecializationData specialization) { for (CacheExpression cache : specialization.getCaches()) { Parameter cacheParameter = cache.getParameter(); String name = cacheParameter.getVariableElement().getSimpleName().toString(); set(cacheParameter.getLocalName(), new LocalVariable(cacheParameter.getType(), name, CodeTreeBuilder.singleString("this." + name), null)); } for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) { String name = assumptionName(assumption); TypeMirror type = assumption.getExpression().getResolvedType(); set(name, new LocalVariable(type, name, CodeTreeBuilder.singleString("this." + name), null)); } } public CodeExecutableElement createMethod(Set<Modifier> modifiers, TypeMirror returnType, String name, int varArgsThreshold, String... optionalArguments) { CodeExecutableElement method = new CodeExecutableElement(modifiers, returnType, name); addParametersTo(method, varArgsThreshold, optionalArguments); return method; } public static LocalContext load(DefaultNodeGenFactory factory, ExecutableTypeData type, int varargsThreshold) { LocalContext context = new LocalContext(factory); 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, null)); } for (NodeFieldData field : factory.node.getFields()) { String fieldName = fieldValueName(field); values.put(fieldName, new LocalVariable(field.getType(), fieldName, factory.accessParent(field.getName()), null)); } 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 (execution.isShortCircuit()) { if (evaluatedIndex < executedType.getEvaluatedCount()) { TypeMirror evaluatedType = evaluatedParameter.get(evaluatedIndex); LocalVariable shortCircuit = createShortCircuitValue(execution).newType(evaluatedType); if (varargs) { shortCircuit = shortCircuit.accessWith(createReadVarargs(evaluatedIndex)); } values.put(shortCircuit.getName(), shortCircuit.makeOriginal()); evaluatedIndex++; } } 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 LocalContext load(DefaultNodeGenFactory factory) { return load(factory, factory.createSpecializationNodeSignature(factory.node.getSignatureSize()), factory.varArgsThreshold); } public LocalContext copy() { LocalContext copy = new LocalContext(factory); copy.values.putAll(values); 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, null); } public LocalVariable createShortCircuitValue(NodeExecutionData execution) { return new LocalVariable(factory.getType(boolean.class), shortCircuitName(execution), null, null); } private static String valueName(NodeExecutionData execution) { return execution.getName() + "Value"; } private static String shortCircuitName(NodeExecutionData execution) { return "has" + ElementUtils.firstLetterUpperCase(valueName(execution)); } public void set(String id, LocalVariable var) { values.put(id, var); } public LocalVariable get(String id) { return values.get(id); } public LocalVariable get(Parameter parameter, int signatureIndex) { LocalVariable var = get(parameter.getLocalName()); if (var == null && parameter.getSpecification().isSignature()) { // lookup by signature index for executeWith List<NodeExecutionData> childExecutions = factory.node.getChildExecutions(); if (signatureIndex < childExecutions.size() && signatureIndex >= 0) { NodeExecutionData execution = childExecutions.get(signatureIndex); var = getValue(execution); } } return var; } 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); } public void setShortCircuitValue(NodeExecutionData execution, LocalVariable var) { if (var == null) { return; } values.put(shortCircuitName(execution), var); } private boolean needsVarargs(boolean requireLoaded, int varArgsThreshold) { int size = 0; for (NodeExecutionData execution : factory.node.getChildExecutions()) { if (requireLoaded && getValue(execution) == null) { continue; } if (execution.isShortCircuit()) { size += 2; } else { 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.nullLiteral(); } else { builder.tree(local.createReference()); } } List<NodeExecutionData> executions = factory.node.getChildExecutions(); for (NodeExecutionData execution : executions) { if (execution.isShortCircuit()) { LocalVariable shortCircuitVar = getShortCircuit(execution); if (shortCircuitVar != null) { builder.tree(shortCircuitVar.createReference()); } } LocalVariable var = getValue(execution); if (var != null) { builder.startGroup(); if (executions.size() == 1 && ElementUtils.typeEquals(var.getTypeMirror(), factory.getType(Object[].class))) { // if the current type is Object[] do not use varargs for a single argument builder.string("(Object) "); } builder.tree(var.createReference()); builder.end(); } } } public void addParametersTo(CodeExecutableElement method, int varArgsThreshold, String... optionalNames) { for (String var : optionalNames) { LocalVariable local = values.get(var); if (local != null) { method.addParameter(local.createParameter()); } } if (needsVarargs(true, varArgsThreshold)) { method.addParameter(new CodeVariableElement(factory.getType(Object[].class), "args_")); method.setVarArgs(true); } else { for (NodeExecutionData execution : factory.node.getChildExecutions()) { if (execution.isShortCircuit()) { LocalVariable shortCircuitVar = getShortCircuit(execution); if (shortCircuitVar != null) { method.addParameter(shortCircuitVar.createParameter()); } } LocalVariable var = getValue(execution); if (var != null) { method.addParameter(var.createParameter()); } } } } private LocalVariable getShortCircuit(NodeExecutionData execution) { return values.get(shortCircuitName(execution)); } @Override public String toString() { return "LocalContext [values=" + values + "]"; } } public static final class LocalVariable { private final TypeMirror typeMirror; private final CodeTree accessorTree; private final String name; private final LocalVariable previous; public static LocalVariable fromParameter(Parameter parameter) { NodeExecutionData execution = parameter.getSpecification().getExecution(); String name = null; if (execution == null) { name = parameter.getLocalName(); } else { name = createName(execution); } return new LocalVariable(parameter.getType(), name, null, null); } private LocalVariable(TypeMirror typeMirror, String name, CodeTree accessorTree, LocalVariable previous) { Objects.requireNonNull(typeMirror); this.typeMirror = typeMirror; this.accessorTree = accessorTree; this.name = name; this.previous = previous; } public String getShortCircuitName() { return "has" + ElementUtils.firstLetterUpperCase(getName()); } public String getName() { return name; } private static String createNextName(String name) { return name + "_"; } private static String createName(NodeExecutionData execution) { if (execution == null) { return "<error>"; } return execution.getName() + "Value"; } 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, this); } public LocalVariable accessWith(CodeTree tree) { return new LocalVariable(typeMirror, name, tree, this); } public LocalVariable nextName() { return new LocalVariable(typeMirror, createNextName(name), accessorTree, this); } public LocalVariable makeOriginal() { return new LocalVariable(typeMirror, name, accessorTree, null); } public LocalVariable original() { LocalVariable variable = this; while (variable.previous != null) { variable = variable.previous; } return variable; } public LocalVariable makeGeneric(ProcessorContext context) { return newType(context.getType(Object.class)); } @Override public String toString() { return "Local[type = " + getTypeMirror() + ", name = " + name + ", accessWith = " + accessorTree + "]"; } } private abstract class SpecializationBody { private final boolean fastPath; private final boolean needsCastedValues; SpecializationBody(boolean fastPath, boolean needsCastedValues) { this.fastPath = fastPath; this.needsCastedValues = needsCastedValues; } public final boolean isFastPath() { return fastPath; } public final boolean needsCastedValues() { return needsCastedValues; } public abstract CodeTree createBody(SpecializationData specialization, LocalContext currentValues); } }