/* * Copyright (c) 2009, 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.max.vm.ext.maxri; import static com.sun.max.platform.Platform.*; import static com.sun.max.vm.MaxineVM.*; import static com.sun.max.vm.compiler.target.Stub.Type.*; import static com.sun.max.vm.stack.VMFrameLayout.*; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.*; import com.oracle.max.asm.target.amd64.*; import com.oracle.max.cri.intrinsics.*; import com.oracle.max.criutils.*; import com.oracle.max.graal.cri.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.nodes.*; import com.oracle.max.graal.nodes.calc.*; import com.oracle.max.graal.nodes.extended.*; import com.oracle.max.vm.ext.maxri.MaxXirGenerator.RuntimeCalls; import com.sun.cri.ci.*; import com.sun.cri.ci.CiTargetMethod.Call; import com.sun.cri.ci.CiTargetMethod.DataPatch; import com.sun.cri.ci.CiTargetMethod.Safepoint; import com.sun.cri.ci.CiUtil.RefMapFormatter; import com.sun.cri.ri.*; import com.sun.max.annotate.*; import com.sun.max.lang.*; import com.sun.max.platform.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.bytecode.*; import com.sun.max.vm.compiler.*; import com.sun.max.vm.compiler.CompilationBroker.*; import com.sun.max.vm.compiler.target.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.ti.*; import com.sun.max.vm.type.*; import com.sun.max.vm.value.*; /** * The {@code MaxRiRuntime} class implements the runtime interface needed by C1X. * This includes access to runtime features such as class and method representations, * constant pools, as well as some compiler tuning. */ public class MaxRuntime implements GraalRuntime { private static MaxRuntime instance = new MaxRuntime(); private IntrinsicImpl.Registry intrinsicRegistry; /** * Gets the global MaxRuntime instance. */ public static MaxRuntime runtime() { return instance; } /** * Factory method for getting a Graal runtime instance. This method is called via reflection. */ public static GraalRuntime getGraalRuntime() { if (MaxineVM.isHosted() && vm() == null) { RuntimeCompiler.optimizingCompilerOption.setValue(NullOptCompiler.class.getName()); RuntimeCompiler.baselineCompilerOption.setValue(NullBaselineCompiler.class.getName()); VMConfigurator.installStandard(BuildLevel.PRODUCT); JavaPrototype.initialize(false); } return runtime(); } private MaxRuntime() { } public void setIntrinsicRegistry(IntrinsicImpl.Registry registry) { intrinsicRegistry = registry; } public IntrinsicImpl.Registry getIntrinsicRegistry() { return intrinsicRegistry; } @HOSTED_ONLY private boolean initialized; @HOSTED_ONLY public void initialize() { if (initialized) { return; } initialized = true; // search for the runtime call and register critical methods for (Method m : RuntimeCalls.class.getDeclaredMethods()) { int flags = m.getModifiers(); if (Modifier.isStatic(flags) && Modifier.isPublic(flags)) { new CriticalMethod(RuntimeCalls.class, m.getName(), SignatureDescriptor.create(m.getReturnType(), m.getParameterTypes())); } } // The direct call made from C1X compiled code for the UNCOMMON_TRAP intrinisic // must go through a stub that saves the register state before calling the deopt routine. CriticalMethod uncommonTrap = new CriticalMethod(MaxRuntimeCalls.class, "uncommonTrap", null); uncommonTrap.classMethodActor.compiledState = new Compilations(null, vm().stubs.genUncommonTrapStub()); } /** * Checks whether the runtime requires inlining of the specified method. * @param method the method to inline * @return {@code true} if the method must be inlined; {@code false} * to allow the compiler to use its own heuristics */ public boolean mustInline(RiResolvedMethod method) { if (!(method instanceof ClassMethodActor)) { return false; } ClassMethodActor methodActor = (ClassMethodActor) method; if (methodActor.isInline()) { return true; } // Remove the indirection through the "access$..." methods generated by the Java // source compiler for the purpose of implementing access to private fields between // in an inner-class relationship. if (MaxineVM.isHosted() && methodActor.isSynthetic() && methodActor.isStatic() && methodActor.name().startsWith("access$")) { return true; } return methodActor.isInline(); } /** * Checks whether the runtime forbids inlining of the specified method. * @param method the method to inline * @return {@code true} if the runtime forbids inlining of the specified method; * {@code false} to allow the compiler to use its own heuristics */ public boolean mustNotInline(RiResolvedMethod method) { final ClassMethodActor classMethodActor = (ClassMethodActor) method; if (classMethodActor.isNative()) { // Native stubs must not be inlined as there is a 1:1 relationship between // a NativeFunction and the TargetMethod from which it is called. This // required so that the exact position of the native function call // site can be recorded in the NativeFunction instance. The call site // is used by return true; } if (isHosted()) { CompilationBroker cb = vm().compilationBroker; String compilerName = cb.compilerFor(classMethodActor); if (compilerName != null && cb.baselineCompiler != null && cb.baselineCompiler.matches(compilerName)) { // Ensure that methods intended to be compiled by the baseline compiler for the // purpose of a JTT test are not inlined. return true; } } // Cannot inline a method that has a breakpoint set. if (VMTI.handler().hasBreakpoints(classMethodActor)) { return true; } return classMethodActor.codeAttribute() == null || classMethodActor.isNeverInline(); } /** * Checks whether the runtime forbids compilation of the specified method. * @param method the method to compile * @return {@code true} if the runtime forbids compilation of the specified method; * {@code false} to allow the compiler to compile the method */ public boolean mustNotCompile(RiResolvedMethod method) { return false; } public int basicObjectLockOffsetInBytes() { // Must not be called if the size of the lock object is 0. throw new InternalError("should not reach here"); } public int sizeOfBasicObjectLock() { // locks are not placed on the stack return 0; } public int sizeOfLockData() { // locks are not placed on the stack return 0; } public int codeOffset() { return CallEntryPoint.OPTIMIZED_ENTRY_POINT.offset(); } @Override public String disassemble(RiResolvedMethod method) { ClassMethodActor classMethodActor = (ClassMethodActor) method; return classMethodActor.format("%f %R %H.%n(%P)") + String.format("%n%s", CodeAttributePrinter.toString(classMethodActor.codeAttribute())); } public String disassemble(byte[] code, long address) { final Platform platform = Platform.platform(); HexCodeFile hcf = new HexCodeFile(code, address, platform.isa.name(), platform.wordWidth().numberOfBits); return HexCodeFileTool.toText(hcf); } @Override public String disassemble(final CiTargetMethod tm) { return disassemble(tm, null); } public String disassemble(CiTargetMethod ciTM, MaxTargetMethod maxTM) { byte[] code = maxTM == null ? Arrays.copyOf(ciTM.targetCode(), ciTM.targetCodeSize()) : maxTM.code(); final Platform platform = Platform.platform(); long startAddress = maxTM == null ? 0L : maxTM.codeStart().toLong(); HexCodeFile hcf = new HexCodeFile(code, startAddress, platform.isa.name(), platform.wordWidth().numberOfBits); HexCodeFile.addAnnotations(hcf, ciTM.annotations()); addExceptionHandlersComment(ciTM, hcf); CiRegister fp; int refMapToFPOffset; if (platform.isa == ISA.AMD64) { fp = AMD64.rsp; refMapToFPOffset = 0; } else { throw FatalError.unimplemented(); } RefMapFormatter slotFormatter = new RefMapFormatter(target().arch, target().spillSlotSize, fp, refMapToFPOffset); for (Safepoint safepoint : ciTM.safepoints) { if (safepoint instanceof Call) { Call call = (Call) safepoint; if (call.debugInfo != null) { hcf.addComment(Safepoints.safepointPosForCall(call.pcOffset, call.size), CiUtil.append(new StringBuilder(100), call.debugInfo, slotFormatter).toString()); } addOperandComment(hcf, call.pcOffset, "{" + call.target + "}"); } else { if (safepoint.debugInfo != null) { hcf.addComment(safepoint.pcOffset, CiUtil.append(new StringBuilder(100), safepoint.debugInfo, slotFormatter).toString()); } addOperandComment(hcf, safepoint.pcOffset, "{safepoint}"); } } for (DataPatch site : ciTM.dataReferences) { hcf.addOperandComment(site.pcOffset, "{" + site.constant + "}"); } return HexCodeFileTool.toText(hcf); } private static void addExceptionHandlersComment(CiTargetMethod tm, HexCodeFile hcf) { if (!tm.exceptionHandlers.isEmpty()) { String nl = HexCodeFile.NEW_LINE; StringBuilder buf = new StringBuilder("------ Exception Handlers ------").append(nl); for (CiTargetMethod.ExceptionHandler e : tm.exceptionHandlers) { buf.append(" "). append(e.pcOffset).append(" -> "). append(e.handlerPos). append(" ").append(e.exceptionType == null ? "<any>" : e.exceptionType). append(nl); } hcf.addComment(0, buf.toString()); } } private static void addOperandComment(HexCodeFile hcf, int pos, String comment) { String oldValue = hcf.addOperandComment(pos, comment); assert oldValue == null : "multiple comments for operand of instruction at " + pos + ": " + comment + ", " + oldValue; } protected static class CachedInvocation { public CachedInvocation(Value[] args, CiConstant result) { this.args = args; this.result = result; } protected final Value[] args; protected final CiConstant result; } /** * Cache to speed up compile-time folding. This works as an invocation of a {@linkplain FOLD foldable} * method is guaranteed to be idempotent with respect its arguments. */ private final ConcurrentHashMap<MethodActor, CachedInvocation> cache = new ConcurrentHashMap<MethodActor, CachedInvocation>(); protected final boolean canonicalizeFoldableMethods() { return true; } public boolean isFoldable(RiResolvedMethod method) { if (canonicalizeFoldableMethods()) { MethodActor methodActor = (MethodActor) method; return Actor.isDeclaredFoldable(methodActor.flags()); } return false; } @Override public CiConstant fold(RiResolvedMethod method, CiConstant[] args) { assert isFoldable(method); MethodActor methodActor = (MethodActor) method; Value[] values; int length = methodActor.descriptor().argumentCount(!methodActor.isStatic()); assert length == args.length; if (length == 0) { values = Value.NONE; } else { values = new Value[length]; for (int i = 0; i < length; ++i) { CiConstant arg = args[i]; if (arg == null) { return null; } Value value; Kind kind; if (!methodActor.isStatic() && i == 0) { kind = methodActor.holder().kind; } else { kind = methodActor.descriptor().parameterDescriptorAt(i - (methodActor.isStatic() ? 0 : 1)).toKind(); } assert WordUtil.ciKind(kind, true) == arg.kind; // Checkstyle: stop switch (kind.asEnum) { case BOOLEAN: value = BooleanValue.from(arg.asBoolean()); break; case BYTE: value = ByteValue.from((byte) arg.asInt()); break; case CHAR: value = CharValue.from((char) arg.asInt()); break; case DOUBLE: value = DoubleValue.from(arg.asDouble()); break; case FLOAT: value = FloatValue.from(arg.asFloat()); break; case INT: value = IntValue.from(arg.asInt()); break; case LONG: value = LongValue.from(arg.asLong()); break; case REFERENCE: value = ReferenceValue.from(arg.asObject()); break; case SHORT: value = ShortValue.from((short) arg.asInt()); break; case WORD: value = WordValue.from(Address.fromLong(arg.asLong())); break; default: throw new IllegalArgumentException(); } // Checkstyle: resume values[i] = value; } } if (!isHosted()) { CachedInvocation cachedInvocation = cache.get(methodActor); if (cachedInvocation != null && Arrays.equals(values, cachedInvocation.args)) { return cachedInvocation.result; } } try { // attempt to invoke the method CiConstant result = methodActor.invoke(values).asCiConstant(); // set the result of this instruction to be the result of invocation notifyMethodFolded(); if (!isHosted()) { cache.put(methodActor, new CachedInvocation(values, result)); } return result; // note that for void, we will have a void constant with value null } catch (IllegalAccessException e) { // folding failed; too bad } catch (InvocationTargetException e) { // folding failed; too bad } catch (ExceptionInInitializerError e) { // folding failed; too bad } return null; } protected void notifyMethodFolded() { } public Object registerCompilerStub(CiTargetMethod ciTargetMethod, String name) { return new Stub(CompilerStub, name, ciTargetMethod); } public RiResolvedType getType(Class<?> javaClass) { return ClassActor.fromJava(javaClass); } public RiResolvedType asRiType(CiKind kind) { return getType(kind.toJavaClass()); } public RiResolvedType getTypeOf(CiConstant constant) { if (constant.kind.isObject()) { Object o = constant.asObject(); if (o != null) { return ClassActor.fromJava(o.getClass()); } } return null; } public Object asJavaObject(CiConstant c) { return c.asObject(); } public Class<?> asJavaClass(CiConstant c) { Object o = c.asObject(); if (o instanceof Class) { return (Class) o; } return null; } @Override public Object asCallTarget(Object target) { if (target instanceof CiRuntimeCall) { target = MaxRuntimeCalls.getClassMethodActor((CiRuntimeCall) target); } else if (target == null) { target = CallTarget.TEMPLATE_CALL; } CallTarget.assertSupportedTarget(target); return target; } public boolean isExceptionType(RiResolvedType type) { return type.isSubtypeOf(getType(Throwable.class)); } public boolean areConstantObjectsEqual(CiConstant x, CiConstant y) { assert x.kind.isObject() && y.kind.isObject(); return x.asObject() == y.asObject(); } public RiRegisterConfig getRegisterConfig(RiMethod method) { return vm().registerConfigs.getRegisterConfig((ClassMethodActor) method); } /** * Reserves a word at the bottom of the frame for saving an overwritten return address during the deoptimization process. */ public int getCustomStackAreaSize() { return STACK_SLOT_SIZE; } public int getArrayLength(CiConstant array) { return Array.getLength(array.asObject()); } public void lower(Node n, CiLoweringTool tool) { 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)); if (load.object().kind() == CiKind.Object) { 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)); if (store.object().kind() == CiKind.Object) { write.setGuard((GuardNode) tool.createGuard(graph.unique(new NullCheckNode(store.object(), false)))); } FixedNode next = store.next(); store.setNext(null); // TODO: add Maxine-specific write barrier // FieldWriteBarrier barrier = graph.add(new FieldWriteBarrier(store.object())); // barrier.setNext(next); // write.setNext(barrier); write.setNext(next); write.setStateAfter(store.stateAfter()); store.replaceAtPredecessors(write); store.safeDelete(); } } public StructuredGraph intrinsicGraph(RiResolvedMethod caller, int bci, RiResolvedMethod method, List< ? extends Node> parameters) { return null; } public long getMaxCallTargetOffset(CiRuntimeCall rtcall) { // TODO(tw): Implement for Maxine. return 0; } public RiResolvedMethod getRiMethod(Method reflectionMethod) { return MethodActor.fromJava(reflectionMethod); } public void installMethod(RiMethod method, CiTargetMethod code) { ClassMethodActor cma = (ClassMethodActor) method; synchronized (cma) { MaxTargetMethod tm = new MaxTargetMethod(cma, code, true); cma.compiledState = new Compilations(null, tm); } } @Override public void executeOnCompilerThread(Runnable r) { throw new UnsupportedOperationException(); } @Override public RiCompiledMethod addMethod(RiResolvedMethod method, CiTargetMethod code) { throw new UnsupportedOperationException(); } }