Mercurial > hg > truffle
diff graal/com.oracle.graal.snippets/src/com/oracle/graal/replacements/SnippetTemplate.java @ 8415:2361bf148c06
rename packages: *snippets* -> *replacements*
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Wed, 20 Mar 2013 22:23:14 +0100 |
parents | graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java@8f274684c123 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/replacements/SnippetTemplate.java Wed Mar 20 22:23:14 2013 +0100 @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.replacements; + +import java.lang.reflect.*; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.loop.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.common.*; +import com.oracle.graal.replacements.Snippet.*; +import com.oracle.graal.replacements.nodes.*; +import com.oracle.graal.word.*; +import com.oracle.graal.word.phases.*; + +/** + * A snippet template is a graph created by parsing a snippet method and then specialized by binding + * constants to the snippet's {@link ConstantParameter} parameters. + * + * Snippet templates can be managed in a {@link Cache}. + */ +public class SnippetTemplate { + + /** + * A snippet template key encapsulates the method from which a snippet was built and the + * arguments used to specialize the snippet. + * + * @see Cache + */ + public static class Key implements Iterable<Map.Entry<String, Object>> { + + public final ResolvedJavaMethod method; + private final HashMap<String, Object> map = new HashMap<>(); + private int hash; + + public Key(ResolvedJavaMethod method) { + this.method = method; + this.hash = method.hashCode(); + } + + public Key add(String name, Object value) { + assert !map.containsKey(name); + map.put(name, value); + hash = hash ^ name.hashCode(); + if (value != null) { + hash *= (value.hashCode() + 1); + } + return this; + } + + public int length() { + return map.size(); + } + + public Object get(String name) { + return map.get(name); + } + + @Override + public Iterator<Entry<String, Object>> iterator() { + return map.entrySet().iterator(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Key) { + Key other = (Key) obj; + return other.method == method && other.map.equals(map); + } + return false; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return MetaUtil.format("%h.%n", method) + map.toString(); + } + + public Set<String> names() { + return map.keySet(); + } + } + + /** + * Arguments used to instantiate a template. + */ + public static class Arguments implements Iterable<Map.Entry<String, Object>> { + + private final HashMap<String, Object> map = new HashMap<>(); + + public static Arguments arguments(String name, Object value) { + return new Arguments().add(name, value); + } + + public Arguments add(String name, Object value) { + assert !map.containsKey(name); + map.put(name, value); + return this; + } + + public int length() { + return map.size(); + } + + @Override + public Iterator<Entry<String, Object>> iterator() { + return map.entrySet().iterator(); + } + + @Override + public String toString() { + return map.toString(); + } + } + + /** + * A collection of snippet templates accessed by a {@link Key} instance. + */ + public static class Cache { + + private final ConcurrentHashMap<SnippetTemplate.Key, SnippetTemplate> templates = new ConcurrentHashMap<>(); + private final MetaAccessProvider runtime; + private final TargetDescription target; + + public Cache(MetaAccessProvider runtime, TargetDescription target) { + this.runtime = runtime; + this.target = target; + } + + /** + * Gets a template for a given key, creating it first if necessary. + */ + public SnippetTemplate get(final SnippetTemplate.Key key, final Assumptions assumptions) { + SnippetTemplate template = templates.get(key); + if (template == null) { + template = Debug.scope("SnippetSpecialization", key.method, new Callable<SnippetTemplate>() { + + @Override + public SnippetTemplate call() throws Exception { + return new SnippetTemplate(runtime, assumptions, target, key); + } + }); + // System.out.println(key + " -> " + template); + templates.put(key, template); + } + return template; + } + } + + public abstract static class AbstractTemplates<T extends Snippets> { + + protected final Cache cache; + protected final MetaAccessProvider runtime; + protected final Assumptions assumptions; + protected Class<?> snippetsClass; + + public AbstractTemplates(MetaAccessProvider runtime, Assumptions assumptions, TargetDescription target, Class<T> snippetsClass) { + this.runtime = runtime; + this.assumptions = assumptions; + if (snippetsClass == null) { + assert this instanceof Snippets; + this.snippetsClass = getClass(); + } else { + this.snippetsClass = snippetsClass; + } + this.cache = new Cache(runtime, target); + } + + protected ResolvedJavaMethod snippet(String name, Class<?>... parameterTypes) { + try { + ResolvedJavaMethod snippet = runtime.lookupJavaMethod(snippetsClass.getDeclaredMethod(name, parameterTypes)); + assert snippet.getAnnotation(Snippet.class) != null : "snippet is not annotated with @" + Snippet.class.getSimpleName(); + return snippet; + } catch (NoSuchMethodException e) { + throw new GraalInternalError(e); + } + } + } + + private static final Object UNUSED_PARAMETER = "DEAD PARAMETER"; + + /** + * Determines if any parameter of a given method is annotated with {@link ConstantParameter}. + */ + public static boolean hasConstantParameter(ResolvedJavaMethod method) { + for (ConstantParameter p : MetaUtil.getParameterAnnotations(ConstantParameter.class, method)) { + if (p != null) { + return true; + } + } + return false; + } + + /** + * Creates a snippet template. + */ + public SnippetTemplate(MetaAccessProvider runtime, Assumptions assumptions, TargetDescription target, SnippetTemplate.Key key) { + ResolvedJavaMethod method = key.method; + assert Modifier.isStatic(method.getModifiers()) : "snippet method must be static: " + method; + Signature signature = method.getSignature(); + + // Copy snippet graph, replacing constant parameters with given arguments + StructuredGraph snippetGraph = (StructuredGraph) method.getCompilerStorage().get(Graph.class); + StructuredGraph snippetCopy = new StructuredGraph(snippetGraph.name, snippetGraph.method()); + IdentityHashMap<Node, Node> replacements = new IdentityHashMap<>(); + replacements.put(snippetGraph.start(), snippetCopy.start()); + + int parameterCount = signature.getParameterCount(false); + assert checkTemplate(runtime, key, parameterCount, method, signature); + + Parameter[] parameterAnnotations = new Parameter[parameterCount]; + VarargsParameter[] varargsParameterAnnotations = new VarargsParameter[parameterCount]; + ConstantNode[] placeholders = new ConstantNode[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + ConstantParameter c = MetaUtil.getParameterAnnotation(ConstantParameter.class, i, method); + if (c != null) { + String name = c.value(); + Object arg = key.get(name); + Kind kind = signature.getParameterKind(i); + Constant constantArg; + if (arg instanceof Constant) { + constantArg = (Constant) arg; + } else { + constantArg = Constant.forBoxed(kind, arg); + } + replacements.put(snippetGraph.getLocal(i), ConstantNode.forConstant(constantArg, runtime, snippetCopy)); + } else { + VarargsParameter vp = MetaUtil.getParameterAnnotation(VarargsParameter.class, i, method); + if (vp != null) { + String name = vp.value(); + Varargs varargs = (Varargs) key.get(name); + Object array = varargs.getArray(); + ConstantNode placeholder = ConstantNode.forObject(array, runtime, snippetCopy); + replacements.put(snippetGraph.getLocal(i), placeholder); + placeholders[i] = placeholder; + varargsParameterAnnotations[i] = vp; + } else { + parameterAnnotations[i] = MetaUtil.getParameterAnnotation(Parameter.class, i, method); + } + } + } + snippetCopy.addDuplicates(snippetGraph.getNodes(), replacements); + + Debug.dump(snippetCopy, "Before specialization"); + if (!replacements.isEmpty()) { + // Do deferred intrinsification of node intrinsics + new NodeIntrinsificationPhase(runtime, new BoxingMethodPool(runtime)).apply(snippetCopy); + new WordTypeRewriterPhase(runtime, target.wordKind).apply(snippetCopy); + + new CanonicalizerPhase(runtime, assumptions, 0, null).apply(snippetCopy); + } + assert NodeIntrinsificationVerificationPhase.verify(snippetCopy); + + // Gather the template parameters + parameters = new HashMap<>(); + for (int i = 0; i < parameterCount; i++) { + VarargsParameter vp = varargsParameterAnnotations[i]; + if (vp != null) { + assert snippetCopy.getLocal(i) == null; + Varargs varargs = (Varargs) key.get(vp.value()); + Object array = varargs.getArray(); + int length = Array.getLength(array); + LocalNode[] locals = new LocalNode[length]; + Stamp stamp = varargs.getArgStamp(); + for (int j = 0; j < length; j++) { + assert (parameterCount & 0xFFFF) == parameterCount; + int idx = i << 16 | j; + LocalNode local = snippetCopy.unique(new LocalNode(idx, stamp)); + locals[j] = local; + } + parameters.put(vp.value(), locals); + + ConstantNode placeholder = placeholders[i]; + assert placeholder != null; + for (Node usage : placeholder.usages().snapshot()) { + if (usage instanceof LoadIndexedNode) { + LoadIndexedNode loadIndexed = (LoadIndexedNode) usage; + Debug.dump(snippetCopy, "Before replacing %s", loadIndexed); + LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(new LoadSnippetVarargParameterNode(locals, loadIndexed.index(), loadIndexed.stamp())); + snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter); + Debug.dump(snippetCopy, "After replacing %s", loadIndexed); + } + } + } else { + Parameter p = parameterAnnotations[i]; + if (p != null) { + LocalNode local = snippetCopy.getLocal(i); + if (local == null) { + // Parameter value was eliminated + parameters.put(p.value(), UNUSED_PARAMETER); + } else { + parameters.put(p.value(), local); + } + } + } + } + + // Do any required loop explosion + boolean exploded = false; + do { + exploded = false; + ExplodeLoopNode explodeLoop = snippetCopy.getNodes().filter(ExplodeLoopNode.class).first(); + if (explodeLoop != null) { // Earlier canonicalization may have removed the loop + // altogether + LoopBeginNode loopBegin = explodeLoop.findLoopBegin(); + if (loopBegin != null) { + LoopEx loop = new LoopsData(snippetCopy).loop(loopBegin); + int mark = snippetCopy.getMark(); + LoopTransformations.fullUnroll(loop, runtime, null); + new CanonicalizerPhase(runtime, assumptions, mark, null).apply(snippetCopy); + } + FixedNode explodeLoopNext = explodeLoop.next(); + explodeLoop.clearSuccessors(); + explodeLoop.replaceAtPredecessor(explodeLoopNext); + explodeLoop.replaceAtUsages(null); + GraphUtil.killCFG(explodeLoop); + exploded = true; + } + } while (exploded); + + // Remove all frame states from inlined snippet graph. Snippets must be atomic (i.e. free + // of side-effects that prevent deoptimizing to a point before the snippet). + ArrayList<StateSplit> curSideEffectNodes = new ArrayList<>(); + ArrayList<ValueNode> curStampNodes = new ArrayList<>(); + for (Node node : snippetCopy.getNodes()) { + if (node instanceof ValueNode && ((ValueNode) node).stamp() == StampFactory.forNodeIntrinsic()) { + curStampNodes.add((ValueNode) node); + } + if (node instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) node; + FrameState frameState = stateSplit.stateAfter(); + if (stateSplit.hasSideEffect()) { + curSideEffectNodes.add((StateSplit) node); + } + if (frameState != null) { + stateSplit.setStateAfter(null); + } + } + } + + new DeadCodeEliminationPhase().apply(snippetCopy); + + assert checkAllVarargPlaceholdersAreDeleted(parameterCount, placeholders); + + this.snippet = snippetCopy; + ReturnNode retNode = null; + StartNode entryPointNode = snippet.start(); + + new DeadCodeEliminationPhase().apply(snippetCopy); + + nodes = new ArrayList<>(snippet.getNodeCount()); + for (Node node : snippet.getNodes()) { + if (node == entryPointNode || node == entryPointNode.stateAfter()) { + // Do nothing. + } else { + nodes.add(node); + if (node instanceof ReturnNode) { + retNode = (ReturnNode) node; + } + } + } + + this.sideEffectNodes = curSideEffectNodes; + this.stampNodes = curStampNodes; + this.returnNode = retNode; + } + + private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, ConstantNode[] placeholders) { + for (int i = 0; i < parameterCount; i++) { + if (placeholders[i] != null) { + assert placeholders[i].isDeleted() : placeholders[i]; + } + } + return true; + } + + private static boolean checkConstantArgument(MetaAccessProvider runtime, final ResolvedJavaMethod method, Signature signature, int i, String name, Object arg, Kind kind) { + ResolvedJavaType type = signature.getParameterType(i, method.getDeclaringClass()).resolve(method.getDeclaringClass()); + if (runtime.lookupJavaType(WordBase.class).isAssignableFrom(type)) { + assert arg instanceof Constant : method + ": word constant parameters must be passed boxed in a Constant value: " + arg; + return true; + } + if (kind == Kind.Object) { + assert arg == null || type.isInstance(Constant.forObject(arg)) : method + ": wrong value type for " + name + ": expected " + type.getName() + ", got " + arg.getClass().getName(); + } else { + assert arg != null && kind.toBoxedJavaClass() == arg.getClass() : method + ": wrong value kind for " + name + ": expected " + kind + ", got " + + (arg == null ? "null" : arg.getClass().getSimpleName()); + } + return true; + } + + private static boolean checkVarargs(final ResolvedJavaMethod method, Signature signature, int i, String name, Varargs varargs) { + Object arg = varargs.getArray(); + ResolvedJavaType type = (ResolvedJavaType) signature.getParameterType(i, method.getDeclaringClass()); + assert type.isArray() : "varargs parameter must be an array type"; + assert type.isInstance(Constant.forObject(arg)) : "value for " + name + " is not a " + MetaUtil.toJavaName(type) + " instance: " + arg; + return true; + } + + /** + * The graph built from the snippet method. + */ + private final StructuredGraph snippet; + + /** + * The named parameters of this template that must be bound to values during instantiation. For + * a parameter that is still live after specialization, the value in this map is either a + * {@link LocalNode} instance or a {@link LocalNode} array. For an eliminated parameter, the + * value is identical to the key. + */ + private final Map<String, Object> parameters; + + /** + * The return node (if any) of the snippet. + */ + private final ReturnNode returnNode; + + /** + * Nodes that inherit the {@link StateSplit#stateAfter()} from the replacee during + * instantiation. + */ + private final ArrayList<StateSplit> sideEffectNodes; + + /** + * The nodes that inherit the {@link ValueNode#stamp()} from the replacee during instantiation. + */ + private final ArrayList<ValueNode> stampNodes; + + /** + * The nodes to be inlined when this specialization is instantiated. + */ + private final ArrayList<Node> nodes; + + /** + * Gets the instantiation-time bindings to this template's parameters. + * + * @return the map that will be used to bind arguments to parameters when inlining this template + */ + private IdentityHashMap<Node, Node> bind(StructuredGraph replaceeGraph, MetaAccessProvider runtime, SnippetTemplate.Arguments args) { + IdentityHashMap<Node, Node> replacements = new IdentityHashMap<>(); + assert args.length() == parameters.size() : "number of args (" + args.length() + ") != number of parameters (" + parameters.size() + ")"; + for (Map.Entry<String, Object> e : args) { + String name = e.getKey(); + Object parameter = parameters.get(name); + assert parameter != null : this + " has no parameter named " + name; + Object argument = e.getValue(); + if (parameter instanceof LocalNode) { + if (argument instanceof ValueNode) { + replacements.put((LocalNode) parameter, (ValueNode) argument); + } else { + Kind kind = ((LocalNode) parameter).kind(); + assert argument != null || kind == Kind.Object : this + " cannot accept null for non-object parameter named " + name; + Constant constant = Constant.forBoxed(kind, argument); + replacements.put((LocalNode) parameter, ConstantNode.forConstant(constant, runtime, replaceeGraph)); + } + } else if (parameter instanceof LocalNode[]) { + LocalNode[] locals = (LocalNode[]) parameter; + int length = locals.length; + List list = null; + Object array = null; + if (argument instanceof List) { + list = (List) argument; + assert list.size() == length : length + " != " + list.size(); + } else { + array = argument; + assert array != null && array.getClass().isArray(); + assert Array.getLength(array) == length : length + " != " + Array.getLength(array); + } + + for (int j = 0; j < length; j++) { + LocalNode local = locals[j]; + assert local != null; + Object value = list != null ? list.get(j) : Array.get(array, j); + if (value instanceof ValueNode) { + replacements.put(local, (ValueNode) value); + } else { + Constant constant = Constant.forBoxed(local.kind(), value); + ConstantNode element = ConstantNode.forConstant(constant, runtime, replaceeGraph); + replacements.put(local, element); + } + } + } else { + assert parameter == UNUSED_PARAMETER : "unexpected entry for parameter: " + name + " -> " + parameter; + } + } + return replacements; + } + + /** + * Logic for replacing a snippet-lowered node at its usages with the return value of the + * snippet. An alternative to the {@linkplain SnippetTemplate#DEFAULT_REPLACER default} + * replacement logic can be used to handle mismatches between the stamp of the node being + * lowered and the stamp of the snippet's return value. + */ + public interface UsageReplacer { + + /** + * Replaces all usages of {@code oldNode} with direct or indirect usages of {@code newNode}. + */ + void replace(ValueNode oldNode, ValueNode newNode); + } + + /** + * Represents the default {@link UsageReplacer usage replacer} logic which simply delegates to + * {@link Node#replaceAtUsages(Node)}. + */ + public static final UsageReplacer DEFAULT_REPLACER = new UsageReplacer() { + + @Override + public void replace(ValueNode oldNode, ValueNode newNode) { + oldNode.replaceAtUsages(newNode); + } + }; + + /** + * Replaces a given fixed node with this specialized snippet. + * + * @param runtime + * @param replacee the node that will be replaced + * @param replacer object that replaces the usages of {@code replacee} + * @param args the arguments to be bound to the flattened positional parameters of the snippet + * @return the map of duplicated nodes (original -> duplicate) + */ + public Map<Node, Node> instantiate(MetaAccessProvider runtime, FixedNode replacee, UsageReplacer replacer, SnippetTemplate.Arguments args) { + + // Inline the snippet nodes, replacing parameters with the given args in the process + String name = snippet.name == null ? "{copy}" : snippet.name + "{copy}"; + StructuredGraph snippetCopy = new StructuredGraph(name, snippet.method()); + StartNode entryPointNode = snippet.start(); + FixedNode firstCFGNode = entryPointNode.next(); + StructuredGraph replaceeGraph = (StructuredGraph) replacee.graph(); + IdentityHashMap<Node, Node> replacements = bind(replaceeGraph, runtime, args); + Map<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, replacements); + Debug.dump(replaceeGraph, "After inlining snippet %s", snippetCopy.method()); + + // Re-wire the control flow graph around the replacee + FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); + replacee.replaceAtPredecessor(firstCFGNodeDuplicate); + FixedNode next = null; + if (replacee instanceof FixedWithNextNode) { + FixedWithNextNode fwn = (FixedWithNextNode) replacee; + next = fwn.next(); + fwn.setNext(null); + } + + if (replacee instanceof StateSplit) { + for (StateSplit sideEffectNode : sideEffectNodes) { + assert ((StateSplit) replacee).hasSideEffect(); + Node sideEffectDup = duplicates.get(sideEffectNode); + ((StateSplit) sideEffectDup).setStateAfter(((StateSplit) replacee).stateAfter()); + } + } + for (ValueNode stampNode : stampNodes) { + Node stampDup = duplicates.get(stampNode); + ((ValueNode) stampDup).setStamp(((ValueNode) replacee).stamp()); + } + + // Replace all usages of the replacee with the value returned by the snippet + ValueNode returnValue = null; + if (returnNode != null) { + if (returnNode.result() instanceof LocalNode) { + returnValue = (ValueNode) replacements.get(returnNode.result()); + } else { + returnValue = (ValueNode) duplicates.get(returnNode.result()); + } + assert returnValue != null || replacee.usages().isEmpty(); + replacer.replace(replacee, returnValue); + + Node returnDuplicate = duplicates.get(returnNode); + if (returnDuplicate.isAlive()) { + returnDuplicate.clearInputs(); + returnDuplicate.replaceAndDelete(next); + } + } + + // Remove the replacee from its graph + replacee.clearInputs(); + replacee.replaceAtUsages(null); + GraphUtil.killCFG(replacee); + + Debug.dump(replaceeGraph, "After lowering %s with %s", replacee, this); + return duplicates; + } + + /** + * Gets a copy of the specialized graph. + */ + public StructuredGraph copySpecializedGraph() { + return snippet.copy(); + } + + /** + * Replaces a given floating node with this specialized snippet. + * + * @param runtime + * @param replacee the node that will be replaced + * @param replacer object that replaces the usages of {@code replacee} + * @param args the arguments to be bound to the flattened positional parameters of the snippet + */ + public void instantiate(MetaAccessProvider runtime, FloatingNode replacee, UsageReplacer replacer, LoweringTool tool, SnippetTemplate.Arguments args) { + + // Inline the snippet nodes, replacing parameters with the given args in the process + String name = snippet.name == null ? "{copy}" : snippet.name + "{copy}"; + StructuredGraph snippetCopy = new StructuredGraph(name, snippet.method()); + StartNode entryPointNode = snippet.start(); + FixedNode firstCFGNode = entryPointNode.next(); + StructuredGraph replaceeGraph = (StructuredGraph) replacee.graph(); + IdentityHashMap<Node, Node> replacements = bind(replaceeGraph, runtime, args); + Map<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, replacements); + Debug.dump(replaceeGraph, "After inlining snippet %s", snippetCopy.method()); + + FixedWithNextNode lastFixedNode = tool.lastFixedNode(); + assert lastFixedNode != null && lastFixedNode.isAlive() : replaceeGraph; + FixedNode next = lastFixedNode.next(); + lastFixedNode.setNext(null); + FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); + replaceeGraph.addAfterFixed(lastFixedNode, firstCFGNodeDuplicate); + + if (replacee instanceof StateSplit) { + for (StateSplit sideEffectNode : sideEffectNodes) { + assert ((StateSplit) replacee).hasSideEffect(); + Node sideEffectDup = duplicates.get(sideEffectNode); + ((StateSplit) sideEffectDup).setStateAfter(((StateSplit) replacee).stateAfter()); + } + } + for (ValueNode stampNode : stampNodes) { + Node stampDup = duplicates.get(stampNode); + ((ValueNode) stampDup).setStamp(((ValueNode) replacee).stamp()); + } + + // Replace all usages of the replacee with the value returned by the snippet + assert returnNode != null : replaceeGraph; + ValueNode returnValue = null; + if (returnNode.result() instanceof LocalNode) { + returnValue = (ValueNode) replacements.get(returnNode.result()); + } else { + returnValue = (ValueNode) duplicates.get(returnNode.result()); + } + assert returnValue != null || replacee.usages().isEmpty(); + replacer.replace(replacee, returnValue); + + tool.setLastFixedNode(null); + Node returnDuplicate = duplicates.get(returnNode); + if (returnDuplicate.isAlive()) { + returnDuplicate.clearInputs(); + returnDuplicate.replaceAndDelete(next); + if (next != null && next.predecessor() instanceof FixedWithNextNode) { + tool.setLastFixedNode((FixedWithNextNode) next.predecessor()); + } + } + + Debug.dump(replaceeGraph, "After lowering %s with %s", replacee, this); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(snippet.toString()).append('('); + String sep = ""; + for (Map.Entry<String, Object> e : parameters.entrySet()) { + String name = e.getKey(); + Object value = e.getValue(); + buf.append(sep); + sep = ", "; + if (value == UNUSED_PARAMETER) { + buf.append("<unused> ").append(name); + } else if (value instanceof LocalNode) { + LocalNode local = (LocalNode) value; + buf.append(local.kind().getJavaName()).append(' ').append(name); + } else { + LocalNode[] locals = (LocalNode[]) value; + String kind = locals.length == 0 ? "?" : locals[0].kind().getJavaName(); + buf.append(kind).append('[').append(locals.length).append("] ").append(name); + } + } + return buf.append(')').toString(); + } + + private static boolean checkTemplate(MetaAccessProvider runtime, SnippetTemplate.Key key, int parameterCount, ResolvedJavaMethod method, Signature signature) { + Set<String> expected = new HashSet<>(); + for (int i = 0; i < parameterCount; i++) { + ConstantParameter c = MetaUtil.getParameterAnnotation(ConstantParameter.class, i, method); + VarargsParameter vp = MetaUtil.getParameterAnnotation(VarargsParameter.class, i, method); + Parameter p = MetaUtil.getParameterAnnotation(Parameter.class, i, method); + if (c != null) { + assert vp == null && p == null; + String name = c.value(); + expected.add(name); + Kind kind = signature.getParameterKind(i); + assert key.names().contains(name) : "key for " + method + " is missing \"" + name + "\": " + key; + assert checkConstantArgument(runtime, method, signature, i, c.value(), key.get(name), kind); + } else if (vp != null) { + assert p == null; + String name = vp.value(); + expected.add(name); + assert key.names().contains(name) : "key for " + method + " is missing \"" + name + "\": " + key; + assert key.get(name) instanceof Varargs; + Varargs varargs = (Varargs) key.get(name); + assert checkVarargs(method, signature, i, name, varargs); + } else { + assert p != null : method + ": parameter " + i + " must be annotated with exactly one of " + "@" + ConstantParameter.class.getSimpleName() + " or " + "@" + + VarargsParameter.class.getSimpleName() + " or " + "@" + Parameter.class.getSimpleName(); + } + } + if (!key.names().containsAll(expected)) { + expected.removeAll(key.names()); + assert false : expected + " missing from key " + key; + } + if (!expected.containsAll(key.names())) { + Set<String> namesCopy = new HashSet<>(key.names()); + namesCopy.removeAll(expected); + assert false : "parameter(s) " + namesCopy + " should be annotated with @" + ConstantParameter.class.getSimpleName() + " or @" + VarargsParameter.class.getSimpleName() + " in " + + MetaUtil.format("%H.%n(%p)", method); + } + return true; + } +}