/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt;
import java.lang.reflect.Constructor;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.VM_Array;
import org.jikesrvm.classloader.VM_Class;
import org.jikesrvm.classloader.VM_FieldReference;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.classloader.VM_Type;
import org.jikesrvm.classloader.VM_TypeReference;
import static org.jikesrvm.compilers.opt.OPT_Constants.RUNTIME_SERVICES_BCI;
import org.jikesrvm.compilers.opt.ir.AStore;
import org.jikesrvm.compilers.opt.ir.Athrow;
import org.jikesrvm.compilers.opt.ir.Call;
import org.jikesrvm.compilers.opt.ir.MonitorOp;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.New;
import org.jikesrvm.compilers.opt.ir.NewArray;
import org.jikesrvm.compilers.opt.ir.OPT_IR;
import org.jikesrvm.compilers.opt.ir.OPT_IRTools;
import org.jikesrvm.compilers.opt.ir.OPT_Inliner;
import org.jikesrvm.compilers.opt.ir.OPT_Instruction;
import org.jikesrvm.compilers.opt.ir.OPT_IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_LocationOperand;
import org.jikesrvm.compilers.opt.ir.OPT_MethodOperand;
import org.jikesrvm.compilers.opt.ir.OPT_Operand;
import org.jikesrvm.compilers.opt.ir.PutStatic;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.ATHROW_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.CALL;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.MONITORENTER_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.MONITOREXIT_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NEWARRAY_UNRESOLVED_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NEWARRAY_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NEWOBJMULTIARRAY_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NEW_UNRESOLVED_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NEW_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PUTFIELD_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PUTSTATIC_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_ASTORE_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_MOVE;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
import org.jikesrvm.compilers.opt.ir.OPT_TypeOperand;
import org.jikesrvm.compilers.opt.ir.PutField;
import org.jikesrvm.memorymanagers.mminterface.MM_Constants;
import org.jikesrvm.memorymanagers.mminterface.MM_Interface;
import org.jikesrvm.objectmodel.VM_ObjectModel;
import org.jikesrvm.runtime.VM_Entrypoints;
/**
* As part of the expansion of HIR into LIR, this compile phase
* replaces all HIR operators that are implemented as calls to
* VM service routines with CALLs to those routines.
* For some (common and performance critical) operators, we
* may optionally inline expand the call (depending on the
* the values of the relevant compiler options and/or VM_Controls).
* This pass is also responsible for inserting write barriers
* if we are using an allocator that requires them. Write barriers
* are always inline expanded.
*/
public final class OPT_ExpandRuntimeServices extends OPT_CompilerPhase {
/**
* Constructor for this compiler phase
*/
private static final Constructor<OPT_CompilerPhase> constructor =
getCompilerPhaseConstructor(OPT_ExpandRuntimeServices.class);
/**
* Get a constructor object for this compiler phase
* @return compiler phase constructor
*/
public Constructor<OPT_CompilerPhase> getClassConstructor() {
return constructor;
}
public boolean shouldPerform(OPT_Options options) {
return true;
}
public String getName() {
return "Expand Runtime Services";
}
public void reportAdditionalStats() {
VM.sysWrite(" ");
VM.sysWrite(container.counter1 / container.counter2 * 100, 2);
VM.sysWrite("% Infrequent RS calls");
}
/**
* Given an HIR, expand operators that are implemented as calls to
* runtime service methods. This method should be called as one of the
* first steps in lowering HIR into LIR.
*
* @param ir The HIR to expand
*/
public void perform(OPT_IR ir) {
ir.gc.resync(); // resync generation context -- yuck...
OPT_Instruction next;
for (OPT_Instruction inst = ir.firstInstructionInCodeOrder(); inst != null; inst = next) {
next = inst.nextInstructionInCodeOrder();
int opcode = inst.getOpcode();
switch (opcode) {
case NEW_opcode: {
OPT_TypeOperand Type = New.getClearType(inst);
VM_Class cls = (VM_Class) Type.getVMType();
OPT_IntConstantOperand hasFinalizer = OPT_IRTools.IC(cls.hasFinalizer() ? 1 : 0);
VM_Method callSite = inst.position.getMethod();
OPT_IntConstantOperand allocator = OPT_IRTools.IC(MM_Interface.pickAllocator(cls, callSite));
OPT_IntConstantOperand align = OPT_IRTools.IC(VM_ObjectModel.getAlignment(cls));
OPT_IntConstantOperand offset = OPT_IRTools.IC(VM_ObjectModel.getOffsetForAlignment(cls));
OPT_Operand tib = OPT_ConvertToLowLevelIR.getTIB(inst, ir, Type);
if (VM.BuildForIA32 && VM.runningVM) {
// shield BC2IR from address constants
OPT_RegisterOperand tmp = ir.regpool.makeTemp(VM_TypeReference.JavaLangObjectArray);
inst.insertBefore(Move.create(REF_MOVE, tmp, tib));
tib = tmp.copyRO();
}
OPT_IntConstantOperand site = OPT_IRTools.IC(MM_Interface.getAllocationSite(true));
VM_Method target = VM_Entrypoints.resolvedNewScalarMethod;
Call.mutate7(inst,
CALL,
New.getClearResult(inst),
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
OPT_IRTools.IC(cls.getInstanceSize()),
tib,
hasFinalizer,
allocator,
align,
offset,
site);
if (ir.options.INLINE_NEW) {
if (inst.getBasicBlock().getInfrequent()) container.counter1++;
container.counter2++;
if (!ir.options.FREQ_FOCUS_EFFORT || !inst.getBasicBlock().getInfrequent()) {
inline(inst, ir);
}
}
}
break;
case NEW_UNRESOLVED_opcode: {
int typeRefId = New.getType(inst).getTypeRef().getId();
VM_Method target = VM_Entrypoints.unresolvedNewScalarMethod;
OPT_IntConstantOperand site = OPT_IRTools.IC(MM_Interface.getAllocationSite(true));
Call.mutate2(inst,
CALL,
New.getClearResult(inst),
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
OPT_IRTools.IC(typeRefId),
site);
}
break;
case NEWARRAY_opcode: {
OPT_TypeOperand Array = NewArray.getClearType(inst);
VM_Array array = (VM_Array) Array.getVMType();
OPT_Operand numberElements = NewArray.getClearSize(inst);
boolean inline = numberElements instanceof OPT_IntConstantOperand;
OPT_Operand width = OPT_IRTools.IC(array.getLogElementSize());
OPT_Operand headerSize = OPT_IRTools.IC(VM_ObjectModel.computeArrayHeaderSize(array));
VM_Method callSite = inst.position.getMethod();
OPT_IntConstantOperand allocator = OPT_IRTools.IC(MM_Interface.pickAllocator(array, callSite));
OPT_IntConstantOperand align = OPT_IRTools.IC(VM_ObjectModel.getAlignment(array));
OPT_IntConstantOperand offset = OPT_IRTools.IC(VM_ObjectModel.getOffsetForAlignment(array));
OPT_Operand tib = OPT_ConvertToLowLevelIR.getTIB(inst, ir, Array);
if (VM.BuildForIA32 && VM.runningVM) {
// shield BC2IR from address constants
OPT_RegisterOperand tmp = ir.regpool.makeTemp(VM_TypeReference.JavaLangObjectArray);
inst.insertBefore(Move.create(REF_MOVE, tmp, tib));
tib = tmp.copyRO();
}
OPT_IntConstantOperand site = OPT_IRTools.IC(MM_Interface.getAllocationSite(true));
VM_Method target = VM_Entrypoints.resolvedNewArrayMethod;
Call.mutate8(inst,
CALL,
NewArray.getClearResult(inst),
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
numberElements,
width,
headerSize,
tib,
allocator,
align,
offset,
site);
if (inline && ir.options.INLINE_NEW) {
if (inst.getBasicBlock().getInfrequent()) container.counter1++;
container.counter2++;
if (!ir.options.FREQ_FOCUS_EFFORT || !inst.getBasicBlock().getInfrequent()) {
inline(inst, ir);
}
}
}
break;
case NEWARRAY_UNRESOLVED_opcode: {
int typeRefId = NewArray.getType(inst).getTypeRef().getId();
OPT_Operand numberElements = NewArray.getClearSize(inst);
VM_Method target = VM_Entrypoints.unresolvedNewArrayMethod;
OPT_IntConstantOperand site = OPT_IRTools.IC(MM_Interface.getAllocationSite(true));
Call.mutate3(inst,
CALL,
NewArray.getClearResult(inst),
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
numberElements,
OPT_IRTools.IC(typeRefId),
site);
}
break;
case NEWOBJMULTIARRAY_opcode: {
int typeRefId = NewArray.getType(inst).getTypeRef().getId();
VM_Method target = VM_Entrypoints.optNewArrayArrayMethod;
VM_Method callSite = inst.position.getMethod();
Call.mutate3(inst,
CALL,
NewArray.getClearResult(inst),
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
OPT_IRTools.IC(callSite.getId()),
NewArray.getClearSize(inst),
OPT_IRTools.IC(typeRefId));
}
break;
case ATHROW_opcode: {
VM_Method target = VM_Entrypoints.athrowMethod;
OPT_MethodOperand methodOp = OPT_MethodOperand.STATIC(target);
methodOp.setIsNonReturningCall(true); // Record the fact that this is a non-returning call.
Call.mutate1(inst, CALL, null, OPT_IRTools.AC(target.getOffset()), methodOp, Athrow.getClearValue(inst));
}
break;
case MONITORENTER_opcode: {
if (ir.options.NO_SYNCHRO) {
inst.remove();
} else {
OPT_Operand ref = MonitorOp.getClearRef(inst);
VM_Type refType = ref.getType().peekType();
if (refType != null && !refType.getThinLockOffset().isMax()) {
VM_Method target = VM_Entrypoints.inlineLockMethod;
Call.mutate2(inst,
CALL,
null,
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
MonitorOp.getClearGuard(inst),
ref,
OPT_IRTools.AC(refType.getThinLockOffset()));
if (inst.getBasicBlock().getInfrequent()) container.counter1++;
container.counter2++;
if (!ir.options.FREQ_FOCUS_EFFORT || !inst.getBasicBlock().getInfrequent()) {
inline(inst, ir);
}
} else {
VM_Method target = VM_Entrypoints.lockMethod;
Call.mutate1(inst,
CALL,
null,
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
MonitorOp.getClearGuard(inst),
ref);
}
}
break;
}
case MONITOREXIT_opcode: {
if (ir.options.NO_SYNCHRO) {
inst.remove();
} else {
OPT_Operand ref = MonitorOp.getClearRef(inst);
VM_Type refType = ref.getType().peekType();
if (refType != null && !refType.getThinLockOffset().isMax()) {
VM_Method target = VM_Entrypoints.inlineUnlockMethod;
Call.mutate2(inst,
CALL,
null,
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
MonitorOp.getClearGuard(inst),
ref,
OPT_IRTools.AC(refType.getThinLockOffset()));
if (inst.getBasicBlock().getInfrequent()) container.counter1++;
container.counter2++;
if (!ir.options.FREQ_FOCUS_EFFORT || !inst.getBasicBlock().getInfrequent()) {
inline(inst, ir);
}
} else {
VM_Method target = VM_Entrypoints.unlockMethod;
Call.mutate1(inst,
CALL,
null,
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
MonitorOp.getClearGuard(inst),
ref);
}
}
}
break;
case REF_ASTORE_opcode: {
if (MM_Constants.NEEDS_WRITE_BARRIER) {
VM_Method target = VM_Entrypoints.arrayStoreWriteBarrierMethod;
OPT_Instruction wb =
Call.create3(CALL,
null,
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
AStore.getArray(inst).copy(),
AStore.getIndex(inst).copy(),
AStore.getValue(inst).copy());
wb.bcIndex = RUNTIME_SERVICES_BCI;
wb.position = inst.position;
inst.replace(wb);
next = wb.nextInstructionInCodeOrder();
if (ir.options.INLINE_WRITE_BARRIER) {
inline(wb, ir, true);
}
}
}
break;
case PUTFIELD_opcode: {
if (MM_Constants.NEEDS_WRITE_BARRIER) {
OPT_LocationOperand loc = PutField.getLocation(inst);
VM_FieldReference field = loc.getFieldRef();
if (!field.getFieldContentsType().isPrimitiveType()) {
VM_Method target = VM_Entrypoints.putfieldWriteBarrierMethod;
OPT_Instruction wb =
Call.create4(CALL,
null,
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
PutField.getClearGuard(inst),
PutField.getRef(inst).copy(),
PutField.getOffset(inst).copy(),
PutField.getValue(inst).copy(),
OPT_IRTools.IC(field.getId()));
wb.bcIndex = RUNTIME_SERVICES_BCI;
wb.position = inst.position;
inst.replace(wb);
next = wb.nextInstructionInCodeOrder();
if (ir.options.INLINE_WRITE_BARRIER) {
inline(wb, ir);
}
}
}
}
break;
case PUTSTATIC_opcode: {
if (MM_Constants.NEEDS_PUTSTATIC_WRITE_BARRIER) {
OPT_LocationOperand loc = PutStatic.getLocation(inst);
VM_FieldReference field = loc.getFieldRef();
if (!field.getFieldContentsType().isPrimitiveType()) {
VM_Method target = VM_Entrypoints.putstaticWriteBarrierMethod;
OPT_Instruction wb =
Call.create3(CALL,
null,
OPT_IRTools.AC(target.getOffset()),
OPT_MethodOperand.STATIC(target),
PutStatic.getOffset(inst).copy(),
PutStatic.getValue(inst).copy(),
OPT_IRTools.IC(field.getId()));
wb.bcIndex = RUNTIME_SERVICES_BCI;
wb.position = inst.position;
inst.replace(wb);
next = wb.nextInstructionInCodeOrder();
if (ir.options.INLINE_WRITE_BARRIER) {
inline(wb, ir);
}
}
}
}
break;
default:
break;
}
}
// If we actually inlined anything, clean up the mess
if (didSomething) {
branchOpts.perform(ir, true);
_os.perform(ir);
}
// signal that we do not intend to use the gc in other phases anymore.
ir.gc.close();
}
/**
* Inline a call instruction
*/
private void inline(OPT_Instruction inst, OPT_IR ir) {
inline(inst, ir, false);
}
/**
* Inline a call instruction
*/
private void inline(OPT_Instruction inst, OPT_IR ir, boolean noCalleeExceptions) {
// Save and restore inlining control state.
// Some options have told us to inline this runtime service,
// so we have to be sure to inline it "all the way" not
// just 1 level.
boolean savedInliningOption = ir.options.INLINE;
boolean savedExceptionOption = ir.options.NO_CALLEE_EXCEPTIONS;
ir.options.INLINE = true;
ir.options.NO_CALLEE_EXCEPTIONS = noCalleeExceptions;
boolean savedOsrGI = ir.options.OSR_GUARDED_INLINING;
ir.options.OSR_GUARDED_INLINING = false;
try {
OPT_InlineDecision inlDec =
OPT_InlineDecision.YES(Call.getMethod(inst).getTarget(), "Expansion of runtime service");
OPT_Inliner.execute(inlDec, ir, inst);
} finally {
ir.options.INLINE = savedInliningOption;
ir.options.NO_CALLEE_EXCEPTIONS = savedExceptionOption;
ir.options.OSR_GUARDED_INLINING = savedOsrGI;
}
didSomething = true;
}
private final OPT_Simple _os = new OPT_Simple(false, false);
private final OPT_BranchOptimizations branchOpts = new OPT_BranchOptimizations(-1, true, true);
private boolean didSomething = false;
//private final OPT_IntConstantOperand OPT_IRTools.IC(int x) { return OPT_IRTools.OPT_IRTools.IC(x); }
//private final OPT_AddressConstantOperand OPT_IRTools.AC(Address x) { return OPT_IRTools.OPT_IRTools.AC(x); }
//private final OPT_AddressConstantOperand OPT_IRTools.AC(Offset x) { return OPT_IRTools.OPT_IRTools.AC(x); }
}