/*
* 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.ppc;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.VM_InterfaceMethodSignature;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.compilers.opt.OPT_Bits;
import org.jikesrvm.compilers.opt.OPT_OptimizingCompilerException;
import org.jikesrvm.compilers.opt.ir.MIR_Binary;
import org.jikesrvm.compilers.opt.ir.MIR_Branch;
import org.jikesrvm.compilers.opt.ir.MIR_Call;
import org.jikesrvm.compilers.opt.ir.MIR_CondBranch;
import org.jikesrvm.compilers.opt.ir.MIR_CondBranch2;
import org.jikesrvm.compilers.opt.ir.MIR_CondCall;
import org.jikesrvm.compilers.opt.ir.MIR_DataLabel;
import org.jikesrvm.compilers.opt.ir.MIR_Load;
import org.jikesrvm.compilers.opt.ir.MIR_LoadUpdate;
import org.jikesrvm.compilers.opt.ir.MIR_LowTableSwitch;
import org.jikesrvm.compilers.opt.ir.MIR_Move;
import org.jikesrvm.compilers.opt.ir.MIR_Unary;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock;
import org.jikesrvm.compilers.opt.ir.OPT_IR;
import org.jikesrvm.compilers.opt.ir.OPT_IRTools;
import org.jikesrvm.compilers.opt.ir.OPT_Instruction;
import org.jikesrvm.compilers.opt.ir.OPT_MethodOperand;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BBEND_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LABEL_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.MIR_LOWTABLESWITCH_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_ADD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_ADDI;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_ADDIS;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BCL;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BCOND;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BCOND2_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BCTR;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BCTRL;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BCTRL_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BL;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_BLRL_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_CMPI;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_DATA_LABEL;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_LAddr;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_LDI;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_LDIS;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_LInt;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_LIntUX;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_MFSPR;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_MTSPR;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PPC_SLWI;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.RESOLVE_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.UNINT_BEGIN_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.UNINT_END_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.YIELDPOINT_BACKEDGE_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.YIELDPOINT_EPILOGUE_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.YIELDPOINT_OSR_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.YIELDPOINT_PROLOGUE_opcode;
import org.jikesrvm.compilers.opt.ir.OPT_Register;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
import org.jikesrvm.compilers.opt.ir.ppc.OPT_PhysicalRegisterSet;
import org.jikesrvm.compilers.opt.ir.ppc.OPT_PowerPCConditionOperand;
import static org.jikesrvm.compilers.opt.ppc.OPT_PhysicalRegisterConstants.LAST_SCRATCH_GPR;
import org.jikesrvm.runtime.VM_Entrypoints;
import org.jikesrvm.scheduler.VM_Thread;
import org.vmmagic.unboxed.Offset;
/**
* Final acts of MIR expansion for the PowerPC architecture.
* Things that are expanded here (immediately before final assembly)
* should only be those sequences that cannot be expanded earlier
* due to difficulty in keeping optimizations from interfering with them.
*/
public abstract class OPT_FinalMIRExpansion extends OPT_IRTools {
/**
* @param ir the IR to expand
* @return upperbound on number of machine code instructions
* that will be generated for this IR
*/
public static int expand(OPT_IR ir) {
int instructionCount = 0;
int conditionalBranchCount = 0;
int machinecodeLength = 0;
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
for (OPT_Instruction p = ir.firstInstructionInCodeOrder(); p != null; p = p.nextInstructionInCodeOrder()) {
p.setmcOffset(-1);
p.scratchObject = null;
switch (p.getOpcode()) {
case MIR_LOWTABLESWITCH_opcode: {
OPT_BasicBlock tableBlock = p.getBasicBlock();
OPT_BasicBlock nextBlock = tableBlock.splitNodeWithLinksAt(p.prevInstructionInCodeOrder(), ir);
nextBlock.firstInstruction().setmcOffset(-1);
OPT_Register regI = MIR_LowTableSwitch.getIndex(p).getRegister();
int NumTargets = MIR_LowTableSwitch.getNumberOfTargets(p);
tableBlock.appendInstruction(MIR_Call.create0(PPC_BL, null, null, nextBlock.makeJumpTarget()));
for (int i = 0; i < NumTargets; i++) {
tableBlock.appendInstruction(MIR_DataLabel.create(PPC_DATA_LABEL, MIR_LowTableSwitch.getClearTarget(p, i)));
}
OPT_Register temp = phys.getGPR(0);
p.insertBefore(MIR_Move.create(PPC_MFSPR, A(temp), A(phys.getLR())));
p.insertBefore(MIR_Binary.create(PPC_SLWI, I(regI), I(regI), IC(2)));
p.insertBefore(MIR_LoadUpdate.create(PPC_LIntUX, I(temp), I(regI), A(temp)));
p.insertBefore(MIR_Binary.create(PPC_ADD, A(regI), A(regI), I(temp)));
p.insertBefore(MIR_Move.create(PPC_MTSPR, A(phys.getCTR()), A(regI)));
MIR_Branch.mutate(p, PPC_BCTR);
instructionCount += NumTargets + 7;
}
break;
case PPC_BCOND2_opcode: {
OPT_RegisterOperand cond = MIR_CondBranch2.getClearValue(p);
p.insertAfter(MIR_CondBranch.create(PPC_BCOND,
cond.copyU2U(),
MIR_CondBranch2.getClearCond2(p),
MIR_CondBranch2.getClearTarget2(p),
MIR_CondBranch2.getClearBranchProfile2(p)));
MIR_CondBranch.mutate(p,
PPC_BCOND,
cond,
MIR_CondBranch2.getClearCond1(p),
MIR_CondBranch2.getClearTarget1(p),
MIR_CondBranch2.getClearBranchProfile1(p));
conditionalBranchCount++;
}
break;
case PPC_BLRL_opcode:
case PPC_BCTRL_opcode: {
// indirect calls. Second part of fast interface invoker expansion
// See also OPT_ConvertToLowlevelIR.java
if (VM.BuildForIMTInterfaceInvocation) {
if (MIR_Call.hasMethod(p)) {
OPT_MethodOperand mo = MIR_Call.getMethod(p);
if (mo.isInterface()) {
VM_InterfaceMethodSignature sig = VM_InterfaceMethodSignature.findOrCreate(mo.getMemberRef());
int signatureId = sig.getId();
OPT_Instruction s;
if (OPT_Bits.fits(signatureId, 16)) {
s = MIR_Unary.create(PPC_LDI, I(phys.getGPR(LAST_SCRATCH_GPR)), IC(signatureId));
p.insertBefore(s);
instructionCount++;
} else {
s =
MIR_Unary.create(PPC_LDIS,
I(phys.getGPR(LAST_SCRATCH_GPR)),
IC(OPT_Bits.PPCMaskUpper16(signatureId)));
p.insertBefore(s);
s =
MIR_Binary.create(PPC_ADDI,
I(phys.getGPR(LAST_SCRATCH_GPR)),
I(phys.getGPR(LAST_SCRATCH_GPR)),
IC(OPT_Bits.PPCMaskLower16(signatureId)));
p.insertBefore(s);
instructionCount += 2;
}
}
}
}
instructionCount++;
}
break;
case LABEL_opcode:
case BBEND_opcode:
case UNINT_BEGIN_opcode:
case UNINT_END_opcode:
// These generate no code, so don't count them.
break;
case RESOLVE_opcode: {
OPT_Register zero = phys.getGPR(0);
OPT_Register JTOC = phys.getJTOC();
OPT_Register CTR = phys.getCTR();
if (VM.VerifyAssertions) {
VM._assert(p.bcIndex >= 0 && p.position != null);
}
Offset offset = VM_Entrypoints.optResolveMethod.getOffset();
if (OPT_Bits.fits(offset, 16)) {
p.insertBefore(MIR_Load.create(PPC_LAddr, A(zero), A(JTOC), IC(OPT_Bits.PPCMaskLower16(offset))));
} else {
if (VM.VerifyAssertions) VM._assert(OPT_Bits.fits(offset, 32)); //not implemented
p.insertBefore(MIR_Binary.create(PPC_ADDIS, A(zero), A(JTOC), IC(OPT_Bits.PPCMaskUpper16(offset))));
p.insertBefore(MIR_Load.create(PPC_LAddr, A(zero), A(zero), IC(OPT_Bits.PPCMaskLower16(offset))));
instructionCount += 1;
}
p.insertBefore(MIR_Move.create(PPC_MTSPR, A(CTR), A(zero)));
instructionCount += 3;
// Because the GC Map code holds a reference to the original
// instruction, it is important that we mutate the last instruction
// because this will be the GC point.
MIR_Call.mutate0(p, PPC_BCTRL, null, null);
break;
}
case YIELDPOINT_PROLOGUE_opcode: {
OPT_Register TSR = phys.getTSR();
OPT_BasicBlock yieldpoint = findOrCreateYieldpointBlock(ir, VM_Thread.PROLOGUE);
// Because the GC Map code holds a reference to the original
// instruction, it is important that we mutate the last instruction
// because this will be the GC point.
MIR_CondCall.mutate0(p,
PPC_BCL,
null,
null,
I(TSR),
OPT_PowerPCConditionOperand.NOT_EQUAL(),
yieldpoint.makeJumpTarget());
p.getBasicBlock().insertOut(yieldpoint);
conditionalBranchCount++;
}
break;
case YIELDPOINT_BACKEDGE_opcode: {
OPT_BasicBlock yieldpoint = findOrCreateYieldpointBlock(ir, VM_Thread.BACKEDGE);
OPT_Register zero = phys.getGPR(0);
OPT_Register TSR = phys.getTSR();
OPT_Register PR = phys.getPR();
Offset offset = VM_Entrypoints.takeYieldpointField.getOffset();
if (VM.VerifyAssertions) VM._assert(OPT_Bits.fits(offset, 16));
p.insertBefore(MIR_Load.create(PPC_LInt, I(zero), A(PR), IC(OPT_Bits.PPCMaskLower16(offset))));
p.insertBefore(MIR_Binary.create(PPC_CMPI, I(TSR), I(zero), IC(0)));
instructionCount += 2;
// Because the GC Map code holds a reference to the original
// instruction, it is important that we mutate the last instruction
// because this will be the GC point.
MIR_CondCall.mutate0(p,
PPC_BCL,
null,
null,
I(TSR),
OPT_PowerPCConditionOperand.GREATER(),
yieldpoint.makeJumpTarget());
p.getBasicBlock().insertOut(yieldpoint);
conditionalBranchCount++;
}
break;
case YIELDPOINT_EPILOGUE_opcode: {
OPT_BasicBlock yieldpoint = findOrCreateYieldpointBlock(ir, VM_Thread.EPILOGUE);
OPT_Register zero = phys.getGPR(0);
OPT_Register TSR = phys.getTSR();
OPT_Register PR = phys.getPR();
Offset offset = VM_Entrypoints.takeYieldpointField.getOffset();
if (VM.VerifyAssertions) VM._assert(OPT_Bits.fits(offset, 16));
p.insertBefore(MIR_Load.create(PPC_LInt, I(zero), A(PR), IC(OPT_Bits.PPCMaskLower16(offset))));
p.insertBefore(MIR_Binary.create(PPC_CMPI, I(TSR), I(zero), IC(0)));
instructionCount += 2;
// Because the GC Map code holds a reference to the original
// instruction, it is important that we mutate the last instruction
// because this will be the GC point.
MIR_CondCall.mutate0(p,
PPC_BCL,
null,
null,
I(TSR),
OPT_PowerPCConditionOperand.NOT_EQUAL(),
yieldpoint.makeJumpTarget());
p.getBasicBlock().insertOut(yieldpoint);
conditionalBranchCount++;
}
break;
case YIELDPOINT_OSR_opcode: {
// unconditionally branch to yield point.
OPT_BasicBlock yieldpoint = findOrCreateYieldpointBlock(ir, VM_Thread.OSROPT);
// Because the GC Map code holds a reference to the original
// instruction, it is important that we mutate the last instruction
// because this will be the GC point.
MIR_Call.mutate0(p, PPC_BL, null, null, yieldpoint.makeJumpTarget());
p.getBasicBlock().insertOut(yieldpoint);
}
instructionCount++;
break;
default:
if (p.operator().isConditionalBranch()) {
conditionalBranchCount++;
} else {
instructionCount++;
}
break;
}
}
// this is conservative but pretty close, especially for
// reasonably sized methods
if ((instructionCount + conditionalBranchCount) > OPT_Assembler.MAX_COND_DISPL) {
machinecodeLength = instructionCount + 2 * conditionalBranchCount;
} else {
machinecodeLength = instructionCount + conditionalBranchCount;
}
if ((machinecodeLength & ~OPT_Assembler.MAX_24_BITS) != 0) {
throw new OPT_OptimizingCompilerException("CodeGen", "method too large to compile:", OPT_Assembler.MAX_24_BITS);
}
return machinecodeLength;
}
/**
* Return a basic block holding the call to a runtime yield service.
* Create a new basic block at the end of the code order if necessary.
*
* @param ir the governing IR
* @param whereFrom is this yieldpoint from the PROLOGUE, EPILOGUE, or a
* BACKEDGE?
*/
static OPT_BasicBlock findOrCreateYieldpointBlock(OPT_IR ir, int whereFrom) {
VM_Method meth = null;
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
OPT_Register zero = phys.getGPR(0);
// first see if the requested block exists. If not, set up some
// state for creating the block
if (whereFrom == VM_Thread.PROLOGUE) {
if (ir.MIRInfo.prologueYieldpointBlock != null) {
return ir.MIRInfo.prologueYieldpointBlock;
} else {
meth = VM_Entrypoints.optThreadSwitchFromPrologueMethod;
}
} else if (whereFrom == VM_Thread.BACKEDGE) {
if (ir.MIRInfo.backedgeYieldpointBlock != null) {
return ir.MIRInfo.backedgeYieldpointBlock;
} else {
meth = VM_Entrypoints.optThreadSwitchFromBackedgeMethod;
}
} else if (whereFrom == VM_Thread.EPILOGUE) {
if (ir.MIRInfo.epilogueYieldpointBlock != null) {
return ir.MIRInfo.epilogueYieldpointBlock;
} else {
meth = VM_Entrypoints.optThreadSwitchFromEpilogueMethod;
}
} else if (whereFrom == VM_Thread.OSROPT) {
if (ir.MIRInfo.osrYieldpointBlock != null) {
return ir.MIRInfo.osrYieldpointBlock;
} else {
meth = VM_Entrypoints.optThreadSwitchFromOsrOptMethod;
}
}
// Not found. create new basic block holding the requested yieldpoint
// method
OPT_BasicBlock result = new OPT_BasicBlock(-1, null, ir.cfg);
ir.cfg.addLastInCodeOrder(result);
OPT_Register JTOC = phys.getJTOC();
OPT_Register CTR = phys.getCTR();
Offset offset = meth.getOffset();
if (OPT_Bits.fits(offset, 16)) {
result.appendInstruction(MIR_Load.create(PPC_LAddr, A(zero), A(JTOC), IC(OPT_Bits.PPCMaskLower16(offset))));
} else {
if (VM.VerifyAssertions) VM._assert(OPT_Bits.fits(offset, 32)); //not implemented
result.appendInstruction(MIR_Binary.create(PPC_ADDIS, A(zero), A(JTOC), IC(OPT_Bits.PPCMaskUpper16(offset))));
result.appendInstruction(MIR_Load.create(PPC_LAddr, A(zero), A(zero), IC(OPT_Bits.PPCMaskLower16(offset))));
}
result.appendInstruction(MIR_Move.create(PPC_MTSPR, A(CTR), A(zero)));
result.appendInstruction(MIR_Branch.create(PPC_BCTR));
// cache the create block and then return it
if (whereFrom == VM_Thread.PROLOGUE) {
ir.MIRInfo.prologueYieldpointBlock = result;
} else if (whereFrom == VM_Thread.BACKEDGE) {
ir.MIRInfo.backedgeYieldpointBlock = result;
} else if (whereFrom == VM_Thread.EPILOGUE) {
ir.MIRInfo.epilogueYieldpointBlock = result;
} else if (whereFrom == VM_Thread.OSROPT) {
ir.MIRInfo.osrYieldpointBlock = result;
}
return result;
}
}