/*
* 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.graal.hotspot;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import com.oracle.max.graal.compiler.*;
import com.oracle.max.graal.cri.*;
import com.oracle.max.graal.graph.*;
import com.oracle.max.graal.hotspot.nodes.*;
import com.oracle.max.graal.nodes.*;
import com.oracle.max.graal.nodes.calc.*;
import com.oracle.max.graal.nodes.extended.*;
import com.oracle.max.graal.nodes.java.*;
import com.oracle.max.graal.snippets.nodes.*;
import com.sun.cri.ci.*;
import com.sun.cri.ci.CiTargetMethod.DataPatch;
import com.sun.cri.ci.CiTargetMethod.Safepoint;
import com.sun.cri.ri.*;
import com.sun.cri.ri.RiType.Representation;
import com.sun.max.asm.dis.*;
import com.sun.max.lang.*;
/**
* CRI runtime implementation for the HotSpot VM.
*/
public class HotSpotRuntime implements GraalRuntime {
private static final long DOUBLENAN_RAW_LONG_BITS = Double.doubleToRawLongBits(Double.NaN);
private static final int FLOATNAN_RAW_INT_BITS = Float.floatToRawIntBits(Float.NaN);
final GraalContext context;
final HotSpotVMConfig config;
final HotSpotRegisterConfig regConfig;
final HotSpotRegisterConfig globalStubRegConfig;
private final Compiler compiler;
// TODO(ls) this is not a permanent solution - there should be a more sophisticated compiler oracle
private HashSet<RiResolvedMethod> notInlineableMethods = new HashSet<RiResolvedMethod>();
private final ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<Runnable>();
public HotSpotRuntime(GraalContext context, HotSpotVMConfig config, Compiler compiler) {
this.context = context;
this.config = config;
this.compiler = compiler;
regConfig = new HotSpotRegisterConfig(config, false);
globalStubRegConfig = new HotSpotRegisterConfig(config, true);
}
@Override
public int codeOffset() {
return 0;
}
public Compiler getCompiler() {
return compiler;
}
@Override
public String disassemble(byte[] code, long address) {
return disassemble(code, new DisassemblyPrinter(false), address);
}
private String disassemble(byte[] code, DisassemblyPrinter disassemblyPrinter, long address) {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
final ISA instructionSet = ISA.AMD64;
Disassembler.disassemble(byteArrayOutputStream, code, instructionSet, WordWidth.BITS_64, address, null, disassemblyPrinter);
return byteArrayOutputStream.toString();
}
@Override
public String disassemble(final CiTargetMethod targetMethod) {
final DisassemblyPrinter disassemblyPrinter = new DisassemblyPrinter(false) {
private String siteInfo(int pcOffset) {
for (Safepoint site : targetMethod.safepoints) {
if (site.pcOffset == pcOffset) {
return "{safepoint}";
}
}
for (DataPatch site : targetMethod.dataReferences) {
if (site.pcOffset == pcOffset) {
return "{" + site.constant + "}";
}
}
return null;
}
@Override
protected String disassembledObjectString(Disassembler disassembler, DisassembledObject disassembledObject) {
final String string = super.disassembledObjectString(disassembler, disassembledObject);
String site = siteInfo(disassembledObject.startPosition());
if (site != null) {
return string + " " + site;
}
return string;
}
};
final byte[] code = Arrays.copyOf(targetMethod.targetCode(), targetMethod.targetCodeSize());
return disassemble(code, disassemblyPrinter, 0L);
}
@Override
public String disassemble(RiResolvedMethod method) {
return "No disassembler available";
}
public Class<?> getJavaClass(CiConstant c) {
return null;
}
@Override
public RiResolvedType asRiType(CiKind kind) {
return (RiResolvedType) compiler.getVMEntries().getType(kind.toJavaClass());
}
@Override
public RiResolvedType getTypeOf(CiConstant constant) {
return (RiResolvedType) compiler.getVMEntries().getRiType(constant);
}
@Override
public boolean isExceptionType(RiResolvedType type) {
return type.isSubtypeOf((RiResolvedType) compiler.getVMEntries().getType(Throwable.class));
}
@Override
public boolean mustInline(RiResolvedMethod method) {
return false;
}
@Override
public boolean mustNotCompile(RiResolvedMethod method) {
return false;
}
@Override
public boolean mustNotInline(RiResolvedMethod method) {
if (notInlineableMethods.contains(method)) {
return true;
}
return Modifier.isNative(method.accessFlags());
}
public void makeNotInlineable(RiResolvedMethod method) {
notInlineableMethods.add(method);
}
@Override
public Object registerCompilerStub(CiTargetMethod targetMethod, String name) {
return HotSpotTargetMethod.installStub(compiler, targetMethod, name);
}
@Override
public int sizeOfLockData() {
// TODO shouldn't be hard coded
return 8;
}
@Override
public int sizeOfBasicObjectLock() {
// TODO shouldn't be hard coded
return 2 * 8;
}
@Override
public int basicObjectLockOffsetInBytes() {
return 8;
}
public boolean isFoldable(RiResolvedMethod method) {
return false;
}
@Override
public CiConstant fold(RiResolvedMethod method, CiConstant[] args) {
return null;
}
@Override
public boolean areConstantObjectsEqual(CiConstant x, CiConstant y) {
return compiler.getVMEntries().compareConstantObjects(x, y);
}
@Override
public RiRegisterConfig getRegisterConfig(RiMethod method) {
return regConfig;
}
/**
* HotSpots needs an area suitable for storing a program counter for temporary use during the deoptimization process.
*/
@Override
public int getCustomStackAreaSize() {
return 8;
}
@Override
public int getArrayLength(CiConstant array) {
return compiler.getVMEntries().getArrayLength(array);
}
@Override
public Class<?> asJavaClass(CiConstant c) {
return (Class<?>) c.asObject();
}
@Override
public Object asJavaObject(CiConstant c) {
return c.asObject();
}
@Override
public void lower(Node n, CiLoweringTool tool) {
if (!GraalOptions.Lower) {
return;
}
if (n instanceof ArrayLengthNode) {
ArrayLengthNode arrayLengthNode = (ArrayLengthNode) n;
SafeReadNode safeReadArrayLength = safeReadArrayLength(arrayLengthNode.graph(), arrayLengthNode.array());
FixedNode nextNode = arrayLengthNode.next();
arrayLengthNode.clearSuccessors();
safeReadArrayLength.setNext(nextNode);
arrayLengthNode.replaceAndDelete(safeReadArrayLength);
safeReadArrayLength.lower(tool);
} else if (n instanceof LoadFieldNode) {
LoadFieldNode field = (LoadFieldNode) n;
if (field.isVolatile()) {
return;
}
Graph graph = field.graph();
int displacement = ((HotSpotField) field.field()).offset();
assert field.kind() != CiKind.Illegal;
ReadNode memoryRead = graph.unique(new ReadNode(field.field().kind(true).stackKind(), field.object(), LocationNode.create(field.field(), field.field().kind(true), displacement, graph)));
memoryRead.setGuard((GuardNode) tool.createGuard(graph.unique(new NullCheckNode(field.object(), false))));
FixedNode next = field.next();
field.setNext(null);
memoryRead.setNext(next);
field.replaceAndDelete(memoryRead);
} else if (n instanceof StoreFieldNode) {
StoreFieldNode field = (StoreFieldNode) n;
if (field.isVolatile()) {
return;
}
Graph graph = field.graph();
int displacement = ((HotSpotField) field.field()).offset();
WriteNode memoryWrite = graph.add(new WriteNode(field.object(), field.value(), LocationNode.create(field.field(), field.field().kind(true), displacement, graph)));
memoryWrite.setGuard((GuardNode) tool.createGuard(graph.unique(new NullCheckNode(field.object(), false))));
memoryWrite.setStateAfter(field.stateAfter());
FixedNode next = field.next();
field.setNext(null);
if (field.field().kind(true) == CiKind.Object && !field.value().isNullConstant()) {
FieldWriteBarrier writeBarrier = graph.add(new FieldWriteBarrier(field.object()));
memoryWrite.setNext(writeBarrier);
writeBarrier.setNext(next);
} else {
memoryWrite.setNext(next);
}
field.replaceAndDelete(memoryWrite);
} else if (n instanceof LoadIndexedNode) {
LoadIndexedNode loadIndexed = (LoadIndexedNode) n;
Graph graph = loadIndexed.graph();
GuardNode boundsCheck = createBoundsCheck(loadIndexed, tool);
CiKind elementKind = loadIndexed.elementKind();
LocationNode arrayLocation = createArrayLocation(graph, elementKind, loadIndexed.index());
ReadNode memoryRead = graph.unique(new ReadNode(elementKind.stackKind(), loadIndexed.array(), arrayLocation));
memoryRead.setGuard(boundsCheck);
FixedNode next = loadIndexed.next();
loadIndexed.setNext(null);
memoryRead.setNext(next);
loadIndexed.replaceAndDelete(memoryRead);
} else if (n instanceof StoreIndexedNode) {
StoreIndexedNode storeIndexed = (StoreIndexedNode) n;
Graph graph = storeIndexed.graph();
AnchorNode anchor = graph.add(new AnchorNode());
GuardNode boundsCheck = createBoundsCheck(storeIndexed, tool);
FixedWithNextNode append = anchor;
CiKind elementKind = storeIndexed.elementKind();
LocationNode arrayLocation = createArrayLocation(graph, elementKind, storeIndexed.index());
ValueNode value = storeIndexed.value();
ValueNode array = storeIndexed.array();
if (elementKind == CiKind.Object && !value.isNullConstant()) {
// Store check!
if (array.exactType() != null) {
RiResolvedType elementType = array.exactType().componentType();
if (elementType.superType() != null) {
ConstantNode type = graph.unique(ConstantNode.forCiConstant(elementType.getEncoding(Representation.ObjectHub), this, graph));
value = graph.unique(new CheckCastNode(anchor, type, elementType, value));
} else {
assert elementType.name().equals("Ljava/lang/Object;") : elementType.name();
}
} else {
GuardNode guard = (GuardNode) tool.createGuard(graph.unique(new NullCheckNode(array, false)));
ReadNode arrayClass = graph.unique(new ReadNode(CiKind.Object, array, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.hubOffset, graph)));
arrayClass.setGuard(guard);
append.setNext(arrayClass);
append = arrayClass;
ReadNode arrayElementKlass = graph.unique(new ReadNode(CiKind.Object, arrayClass, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.arrayClassElementOffset, graph)));
value = graph.unique(new CheckCastNode(anchor, arrayElementKlass, null, value));
}
}
WriteNode memoryWrite = graph.add(new WriteNode(array, value, arrayLocation));
memoryWrite.setGuard(boundsCheck);
memoryWrite.setStateAfter(storeIndexed.stateAfter());
FixedNode next = storeIndexed.next();
storeIndexed.setNext(null);
append.setNext(memoryWrite);
if (elementKind == CiKind.Object && !value.isNullConstant()) {
ArrayWriteBarrier writeBarrier = graph.add(new ArrayWriteBarrier(array, arrayLocation));
memoryWrite.setNext(writeBarrier);
writeBarrier.setNext(next);
} else {
memoryWrite.setNext(next);
}
storeIndexed.replaceAtPredecessors(anchor);
storeIndexed.safeDelete();
} else if (n instanceof UnsafeLoadNode) {
UnsafeLoadNode load = (UnsafeLoadNode) n;
Graph graph = load.graph();
assert load.kind() != CiKind.Illegal;
IndexedLocationNode location = IndexedLocationNode.create(LocationNode.ANY_LOCATION, load.loadKind(), load.displacement(), load.offset(), graph);
location.setIndexScalingEnabled(false);
ReadNode memoryRead = graph.unique(new ReadNode(load.kind(), load.object(), location));
memoryRead.setGuard((GuardNode) tool.createGuard(graph.unique(new NullCheckNode(load.object(), false))));
FixedNode next = load.next();
load.setNext(null);
memoryRead.setNext(next);
load.replaceAndDelete(memoryRead);
} else if (n instanceof UnsafeStoreNode) {
UnsafeStoreNode store = (UnsafeStoreNode) n;
Graph graph = store.graph();
IndexedLocationNode location = IndexedLocationNode.create(LocationNode.ANY_LOCATION, store.storeKind(), store.displacement(), store.offset(), graph);
location.setIndexScalingEnabled(false);
WriteNode write = graph.add(new WriteNode(store.object(), store.value(), location));
FieldWriteBarrier barrier = graph.add(new FieldWriteBarrier(store.object()));
FixedNode next = store.next();
store.setNext(null);
barrier.setNext(next);
write.setNext(barrier);
write.setStateAfter(store.stateAfter());
store.replaceAtPredecessors(write);
store.safeDelete();
} else if (n instanceof ArrayHeaderSizeNode) {
n.replaceAndDelete(ConstantNode.forLong(config.getArrayOffset(((ArrayHeaderSizeNode) n).elementKind()), n.graph()));
}
}
private IndexedLocationNode createArrayLocation(Graph graph, CiKind elementKind, ValueNode index) {
return IndexedLocationNode.create(LocationNode.getArrayLocation(elementKind), elementKind, config.getArrayOffset(elementKind), index, graph);
}
private GuardNode createBoundsCheck(AccessIndexedNode n, CiLoweringTool tool) {
return (GuardNode) tool.createGuard(n.graph().unique(new CompareNode(n.index(), Condition.BT, n.length())));
}
@Override
public StructuredGraph intrinsicGraph(RiResolvedMethod caller, int bci, RiResolvedMethod method, List<? extends Node> parameters) {
RiType holder = method.holder();
String fullName = method.name() + method.signature().asString();
String holderName = holder.name();
if (holderName.equals("Ljava/lang/Object;")) {
if (fullName.equals("getClass()Ljava/lang/Class;")) {
ValueNode obj = (ValueNode) parameters.get(0);
if (obj.stamp().nonNull() && obj.stamp().exactType() != null) {
StructuredGraph graph = new StructuredGraph();
ValueNode result = ConstantNode.forObject(obj.stamp().exactType().toJava(), this, graph);
ReturnNode ret = graph.add(new ReturnNode(result));
graph.start().setNext(ret);
return graph;
}
StructuredGraph graph = new StructuredGraph();
LocalNode receiver = graph.unique(new LocalNode(CiKind.Object, 0));
SafeReadNode klassOop = safeReadHub(graph, receiver);
ReadNode result = graph.add(new ReadNode(CiKind.Object, klassOop, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.classMirrorOffset, graph)));
ReturnNode ret = graph.add(new ReturnNode(result));
graph.start().setNext(klassOop);
klassOop.setNext(ret);
return graph;
}
} else if (holderName.equals("Ljava/lang/Class;")) {
if (fullName.equals("getModifiers()I")) {
StructuredGraph graph = new StructuredGraph();
LocalNode receiver = graph.unique(new LocalNode(CiKind.Object, 0));
SafeReadNode klassOop = safeRead(graph, CiKind.Object, receiver, config.klassOopOffset);
graph.start().setNext(klassOop);
// TODO(tw): Care about primitive classes!
ReadNode result = graph.unique(new ReadNode(CiKind.Int, klassOop, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Int, config.klassModifierFlagsOffset, graph)));
ReturnNode ret = graph.add(new ReturnNode(result));
klassOop.setNext(ret);
return graph;
}
} else if (holderName.equals("Ljava/lang/Thread;")) {
if (fullName.equals("currentThread()Ljava/lang/Thread;")) {
StructuredGraph graph = new StructuredGraph();
ReturnNode ret = graph.add(new ReturnNode(graph.unique(new CurrentThread(config.threadObjectOffset))));
graph.start().setNext(ret);
return graph;
}
}
return null;
}
private boolean containsGraph(RiResolvedMethod method) {
return method.compilerStorage().containsKey(Graph.class);
}
private SafeReadNode safeReadHub(Graph graph, ValueNode value) {
return safeRead(graph, CiKind.Object, value, config.hubOffset);
}
private SafeReadNode safeReadArrayLength(Graph graph, ValueNode value) {
return safeRead(graph, CiKind.Int, value, config.arrayLengthOffset);
}
private SafeReadNode safeRead(Graph graph, CiKind kind, ValueNode value, int offset) {
return graph.add(new SafeReadNode(kind, value, LocationNode.create(LocationNode.FINAL_LOCATION, kind, offset, graph)));
}
public RiResolvedType getType(Class<?> clazz) {
return (RiResolvedType) compiler.getVMEntries().getType(clazz);
}
public Object asCallTarget(Object target) {
return target;
}
public long getMaxCallTargetOffset(CiRuntimeCall rtcall) {
return compiler.getVMEntries().getMaxCallTargetOffset(rtcall);
}
public RiResolvedMethod getRiMethod(Method reflectionMethod) {
return (RiResolvedMethod) compiler.getVMEntries().getRiMethod(reflectionMethod);
}
public void installMethod(RiMethod method, CiTargetMethod code) {
HotSpotTargetMethod.installMethod(CompilerImpl.getInstance(), (HotSpotMethodResolved) method, code, true);
}
@Override
public void executeOnCompilerThread(Runnable r) {
tasks.add(r);
compiler.getVMEntries().notifyJavaQueue();
}
public void pollJavaQueue() {
Runnable r = tasks.poll();
while (r != null) {
try {
r.run();
} catch (Throwable t) {
t.printStackTrace();
}
r = tasks.poll();
}
}
@Override
public RiCompiledMethod addMethod(RiResolvedMethod method, CiTargetMethod code) {
Compiler compilerInstance = CompilerImpl.getInstance();
return HotSpotTargetMethod.installMethod(compilerInstance, (HotSpotMethodResolved) method, code, false);
}
}