/*
* Copyright (c) 2011, 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.max.vm.ext.graal;
import java.lang.reflect.*;
import java.util.*;
import com.oracle.max.cri.intrinsics.*;
import com.oracle.max.graal.graph.*;
import com.oracle.max.graal.nodes.*;
import com.sun.cri.ci.*;
import com.sun.cri.ri.*;
import com.sun.max.annotate.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.compiler.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.type.*;
/**
* Base class for intrinsic implementations targeting Graal. The intrinsic has access to the graph so that
* it can append new instructions to the instruction stream.
*/
public class GraalIntrinsicImpl implements IntrinsicImpl {
/**
* The name of the method called to actually create the graph. This method is found using reflection.
*/
public final String CREATE_NAME = "create";
public final Method createMethod;
/**
* Creates the graph nodes necessary for the implementation of the intrinsic and appends them to the supplied {@link StructuredGraph}.
* <br>
* This default implementation searches for a method with the name "create" and the following parameter list:
* <pre>
* ValueNode create([StructureGraph], [RiResolvedMethod], [RiRuntime], { ValueNode | any_type })
* </pre>
* This means that that the graph, method, and runtime parameters are optionally given to the called method. The args parameter
* is flattened from the NodeList to individual ValueNode parameters. If the parameter has any other type, then the
* node must be a constant, and the constant value is passed instead of the node.
* <br>
* Subclasses can also override this method if they want to avoid the reflective method invocation done in this implementation.
*
* @param graph The graph that the intrinsic will be created into.
* @param method The intrinsic method, i.e., the method that has the {@link INTRINSIC} annotation.
* @param runtime The RiRuntime, used to get information about types, etc.
* @param args The arguments of the intrinsic methods, to be used as the parameters of the intrinsic instruction.
* @return The instruction that should substitute the original method call that is intrinsified.
*/
public ValueNode createGraph(StructuredGraph graph, RiResolvedMethod method, NodeList<ValueNode> args) {
Class[] formalParams = createMethod.getParameterTypes();
Object[] actualParams = new Object[formalParams.length];
int offset = 0;
offset = assignParam(offset, formalParams, actualParams, StructuredGraph.class, graph);
offset = assignParam(offset, formalParams, actualParams, RiResolvedMethod.class, method);
if (offset + args.size() != actualParams.length) {
throw new CiBailout("intrinsic has wrong number of parameters for invoke " + method);
}
for (int i = offset; i < actualParams.length; i++) {
ValueNode node = args.get(i - offset);
if (formalParams[i] != ValueNode.class) {
if (!node.isConstant()) {
throw new CiBailout("intrinsic parameter " + (i - offset) + " must be compile time constant for invoke " + method);
}
actualParams[i] = node.asConstant().boxedValue(WordUtil.ciKind(Kind.fromJava(formalParams[i]), false));
} else {
actualParams[i] = node;
}
}
try {
return (ValueNode) createMethod.invoke(this, actualParams);
} catch (Exception ex) {
throw new CiBailout("intrinsic exception for invoke " + method + " " + Arrays.toString(actualParams), ex);
}
}
private int assignParam(int offset, Class[] formalParams, Object[] actualParams, Class paramClass, Object paramValue) {
if (offset < formalParams.length && formalParams[offset] == paramClass) {
actualParams[offset] = paramValue;
offset++;
}
return offset;
}
@HOSTED_ONLY
private Method getCreateGraphMethod() {
try {
return getClass().getMethod("createGraph", StructuredGraph.class, RiResolvedMethod.class, NodeList.class);
} catch (Exception e) {
throw FatalError.unexpected("Could not find createGraph method in hierarchy of " + getClass(), e);
}
}
@HOSTED_ONLY
protected GraalIntrinsicImpl() {
Method createGraphMethod = getCreateGraphMethod();
// Only look for "create(...)" if createGraph() was not overidden
if (createGraphMethod.getDeclaringClass() == GraalIntrinsicImpl.class) {
int count = 0;
Method createMethod = null;
for (Method m : getClass().getMethods()) {
if (CREATE_NAME.equals(m.getName()) && m.getReturnType() == ValueNode.class) {
createMethod = m;
count++;
}
}
FatalError.check(count == 1, "Expected 1 create method, but found " + count + " in " + this);
this.createMethod = createMethod;
// Ensures that the 'createMethod' is compiled into the boot image
new CriticalMethod(ClassMethodActor.fromJava(createMethod), CallEntryPoint.OPTIMIZED_ENTRY_POINT);
} else {
createMethod = null;
}
}
}