/*
* 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.sun.max.vm.compiler.target;
import static com.sun.max.vm.MaxineVM.*;
import static com.sun.max.vm.VMOptions.*;
import java.io.*;
import java.util.*;
import com.oracle.max.asm.*;
import com.sun.cri.ci.*;
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.member.*;
import com.sun.max.vm.compiler.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.stack.*;
import com.sun.max.vm.type.*;
/**
* Generator for {@linkplain Adapter adapters}, code stubs interposing a call from a caller that has a different
* parameter passing convention from the callee.
*/
public abstract class AdapterGenerator {
@HOSTED_ONLY
public static void init() {
ISA isa = Platform.platform().isa;
String className = Classes.getPackageName(AdapterGenerator.class) + "." + isa.toString().toLowerCase() + "." + isa + AdapterGenerator.class.getSimpleName();
Classes.forName(className);
}
/**
* Local alias to {@link VMFrameLayout#STACK_SLOT_SIZE}.
*/
public static final int OPT_SLOT_SIZE = VMFrameLayout.STACK_SLOT_SIZE;
/**
* Local alias to {@link JVMSFrameLayout#JVMS_SLOT_SIZE}.
*/
public static final int BASELINE_SLOT_SIZE = JVMSFrameLayout.JVMS_SLOT_SIZE;
/**
* A signature denotes the parameter kinds of a call including the receiver kind if applicable.
* The result kind is omitted from the signature as all calling conventions use the same
* location for holding this value.
*/
public static class Sig {
public final Kind[] kinds;
public Sig(MethodActor method) {
kinds = method.getParameterKinds();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Sig) {
Sig sig = (Sig) obj;
return Arrays.equals(kinds, sig.kinds);
}
return false;
}
@Override
public int hashCode() {
if (kinds.length == 0) {
return kinds.length;
}
return kinds.length ^ kinds[kinds.length - 1].character;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(kinds.length + 2);
buf.append('(');
for (Kind k : kinds) {
buf.append(k.character);
}
return buf.append(')').toString();
}
}
/**
* The adapters that have been generated by this generator.
*/
final HashMap<Sig, Adapter> adapters = new HashMap<Sig, Adapter>(100);
/**
* The type of an adapter generated by this generator.
*/
public final Adapter.Type adapterType;
protected final CiRegisterConfig opt;
/**
* Creates an adapter generator for a given adapter type.
*
* @param adapterType the type of adapter produced by this generator
*/
protected AdapterGenerator(Adapter.Type adapterType) {
opt = vm().registerConfigs.standard;
this.adapterType = adapterType;
AdapterGenerator old = generatorsByCallee.put(adapterType.callee, this);
assert old == null;
}
/**
* Gets the number of bytes occupied in an adapter frame for a set of arguments.
*
* @param argKinds the kinds of the arguments
* @param slotSize the adapter frame slot size of a {@linkplain Kind#isCategory1 category 1} kind
*/
public static int frameSizeFor(Kind[] argKinds, int slotSize) {
int size = 0;
for (Kind k : argKinds) {
size += k.stackSlots * slotSize;
}
return size;
}
private static final EnumMap<CallEntryPoint, AdapterGenerator> generatorsByCallee = new EnumMap<CallEntryPoint, AdapterGenerator>(CallEntryPoint.class);
/**
* Gets an adapter generator based on a given callee.
*
* @param callee the method that must be adapted to
* @return the generator for an adapter that adapts a call to {@code callee} from a method that is compiled with the
* other calling convention. The value {@code null} is returned if {@code callee} is itself an adapter, is
* never called by code compiled by the VM (e.g. JNI functions and templates) or if there is only one
* compiler configured for the VM.
*/
public static AdapterGenerator forCallee(TargetMethod callee) {
if (callee.classMethodActor == null) {
// Some kind of stub
return null;
}
return forCallee(callee.classMethodActor, callee.callEntryPoint);
}
public static int prologueSizeForCallee(TargetMethod callee) {
if (callee.classMethodActor == null) {
return 0;
}
AdapterGenerator generator = forCallee(callee.classMethodActor, callee.callEntryPoint);
if (generator == null) {
return 0;
}
return generator.prologueSizeForCallee(callee.classMethodActor);
}
/**
* The code size of a given callee's prologue {@linkplain #emitPrologue(Object, Adapter) emitted} by this generator.
*
* @param callee a callee whose prologue was produced by this generator
* @return the code size of the prologue in callee emitted by this generator
*/
public abstract int prologueSizeForCallee(ClassMethodActor callee);
/**
* Gets an adapter generator based on a given callee.
*
* @param callee the method that must be adapted to
* @param callingConvention the calling convention that {@code callee} is being compiled with
* @return the generator for an adapter that adapts a call to {@code callee} from a method that is compiled
* with the other calling convention. The value {@code null} is returned if {@code callee} is never
* called by code compiled by the VM (e.g. JNI functions and templates) or if there is only one
* compiler configured for the VM.
*/
public static AdapterGenerator forCallee(ClassMethodActor callee, CallEntryPoint callingConvention) {
if (!vm().compilationBroker.needsAdapters()) {
// Only one calling convention; no adapters in use
return null;
}
if (callee != null && (callee.isTemplate() || callee.isVmEntryPoint())) {
// Templates do not have adapters as they are not complete methods that are called
return null;
}
return generatorsByCallee.get(callingConvention);
}
/**
* Gets an adapter based on a given callee, creating it first if necessary.
*
* @param callee the method that must be adapted to
* @return an adapter for a call to {@code callee} from a method compiled with a different calling convention. This
* will be {@code null} if there is no need to adapt a call to {@code callee} (e.g. OPT -> baseline call with 0
* arguments)
*/
public final Adapter make(ClassMethodActor callee) {
SignatureDescriptor signature = callee.descriptor();
int flags = callee.flags();
boolean isStatic = Actor.isStatic(flags);
if (signature.numberOfParameters() == 0 && isStatic) {
if (adapterType == Adapter.Type.OPT2BASELINE) {
return null;
}
// BASELINE2OPT parameterless calls still require an adapter to save and restore the frame pointer
}
// Access to table of adapters must be synchronized
synchronized (this) {
Sig sig = new Sig(callee);
Adapter adapter = adapters.get(sig);
if (adapter == null) {
if (verboseOption.verboseCompilation) {
boolean lockDisabledSafepoints = Log.lock();
Log.printCurrentThread(false);
Log.print(": Creating adapter ");
Log.println(sig);
Log.unlock(lockDisabledSafepoints);
}
adapter = create(sig);
if (verboseOption.verboseCompilation) {
boolean lockDisabledSafepoints = Log.lock();
Log.printCurrentThread(false);
Log.print(": Created adapter ");
Log.println(adapter.regionName());
Log.unlock(lockDisabledSafepoints);
}
adapters.put(sig, adapter);
}
return adapter;
}
}
/**
* Creates code to adapt a call to a given callee. This entails ensuring an adapter exists for the signature of the
* call as well as emitting code for the prologue of the callee to call the adapter.
*
* @param callee the method that must be adapted to
* @param out where to emit the prologue. This must be either an {@link AbstractAssembler} or {@link OutputStream} instance
* @return the adapter that will adapt a call to {@code callee}. This will be {@code null} if there is no need to
* adapt a call to {@code callee} (e.g. OPT -> baseline call with 0 arguments)
*/
public final Adapter adapt(ClassMethodActor callee, Object out) {
Adapter adapter = make(callee);
int prologueSize = emitPrologue(out, adapter);
assert adapter == null || prologueSize == prologueSizeForCallee(callee);
return adapter;
}
/**
* If a given stack frame walker cursor denotes a position in the adapter prologue of a target method,
* then the cursor is advanced to the next frame.
*
* @param current the current stack frame cursor
* @return true if the cursor was advanced
*/
public abstract boolean advanceIfInPrologue(StackFrameCursor current);
/**
* Emits the prologue that makes a calls to a given adapter.
*
* @param out where to emit the prologue. This must be either an {@link AbstractAssembler} or {@link OutputStream} instance
* @param adapter the adapter that the prologue calls
* @return the size of the prologue emitted to {@code out}
*/
protected abstract int emitPrologue(Object out, Adapter adapter);
/**
* Helper method for {@link #emitPrologue(Object, Adapter)} to copy output from an assembler's {@linkplain Buffer buffer} to an
* {@link OutputStream} object.
*
* @param buffer the code buffer into which a prologue has been assembled
* @param out if this is an {@link OutputStream} instance, then the output is written to it
*/
protected void copyIfOutputStream(Buffer buffer, Object out) {
if (out instanceof OutputStream) {
try {
((OutputStream) out).write(buffer.close(true));
} catch (Exception e) {
throw FatalError.unexpected(null, e);
}
}
}
/**
* Determines if a given instruction pointer denotes a position in a given method's adapter prologue.
*/
public boolean inPrologue(CodePointer ip, TargetMethod targetMethod) {
return targetMethod.classMethodActor != null && ip.minus(targetMethod.codeStart()).toPointer().lessThan(prologueSizeForCallee(targetMethod.classMethodActor));
}
/**
* Creates an adapter based on a given signature.
*/
protected abstract Adapter create(Sig sig);
}