/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * 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/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.ppc; import static org.jikesrvm.compilers.common.assembler.ppc.AssemblerConstants.GT; import static org.jikesrvm.compilers.common.assembler.ppc.AssemblerConstants.LT; import static org.jikesrvm.ppc.BaselineConstants.S0; import static org.jikesrvm.ppc.BaselineConstants.S1; import static org.jikesrvm.ppc.BaselineConstants.T0; import static org.jikesrvm.ppc.RegisterConstants.LG_INSTRUCTION_WIDTH; import org.jikesrvm.VM; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.compilers.common.CodeArray; import org.jikesrvm.compilers.common.assembler.ppc.Assembler; import org.jikesrvm.runtime.Magic; import org.jikesrvm.runtime.Memory; /** * Generates a custom IMT-conflict resolution stub. * We create a binary search tree. */ public abstract class InterfaceMethodConflictResolver { /** * Creates a conflict resolution stub for the set of interface method signatures {@code l}. * @param sigIds ids of elements in {@code l} * @param targets target methods of elements in {@code l} * @return code that implements the stub */ public static CodeArray createStub(int[] sigIds, RVMMethod[] targets) { // (1) Create an assembler. int numEntries = sigIds.length; Assembler asm = new Assembler(numEntries); // pretend each entry is a bytecode // (2) signatures must be in ascending order (to build binary search tree). if (VM.VerifyAssertions) { for (int i = 1; i < sigIds.length; i++) { VM._assert(sigIds[i - 1] < sigIds[i]); } } // (3) Assign synthetic bytecode numbers to each switch such that we'll generate them // in ascending order. This lets us use the general forward branching mechanisms // of the Assembler. int[] bcIndices = new int[numEntries]; assignBytecodeIndices(0, bcIndices, 0, numEntries - 1); // (4) Generate the stub. insertStubPrologue(asm); insertStubCase(asm, sigIds, targets, bcIndices, 0, numEntries - 1); CodeArray stub = asm.getMachineCodes(); // (5) synchronize icache with generated machine code that was written through dcache if (VM.runningVM) Memory.sync(Magic.objectAsAddress(stub), stub.length() << LG_INSTRUCTION_WIDTH); return stub; } /** * Assign ascending bytecode indices to each case (in the order they will be generated) * @param bcIndex byte code index for the case in the search tree that will have an index assigned now * @param bcIndices array of byte code indices. All entries in the array are zero at the start * and will be filled in as the generation progresses. * @param low lower bound of the cases to have an index assigned * @param high upper bound of the cases to have an index assigned * @return byte code index to use for the next case in the search tree */ private static int assignBytecodeIndices(int bcIndex, int[] bcIndices, int low, int high) { int middle = (high + low) / 2; bcIndices[middle] = bcIndex++; if (low == middle && middle == high) { return bcIndex; } else { // Recurse. if (low < middle) { bcIndex = assignBytecodeIndices(bcIndex, bcIndices, low, middle - 1); } if (middle < high) { bcIndex = assignBytecodeIndices(bcIndex, bcIndices, middle + 1, high); } return bcIndex; } } /** * Make a stub prologue: get TIB into S0 factor out to reduce code space in each call. * * @param asm assembler that's used for insertion of stub prologue */ private static void insertStubPrologue(Assembler asm) { asm.baselineEmitLoadTIB(S0, T0); } /** * Generates a subtree covering from {@code low} to {@code high} inclusive. * @param asm assembler to use for generation * @param sigIds ids of all the InterfaceMethodSignature instances * @param targets targets of all the InterfaceMethodSignature instances * @param bcIndices array of byte code indices (already filled out) * @param low lower bound of the cases to be generated * @param high upper bound of the cases to be generated */ private static void insertStubCase(Assembler asm, int[] sigIds, RVMMethod[] targets, int[] bcIndices, int low, int high) { int middle = (high + low) / 2; asm.resolveForwardReferences(bcIndices[middle]); if (low == middle && middle == high) { // a leaf case; can simply invoke the method directly. RVMMethod target = targets[middle]; if (target.isStatic()) { // an error case. asm.emitLAddrToc(S0, target.getOffset()); } else { asm.emitLAddrOffset(S0, S0, target.getOffset()); } asm.emitMTCTR(S0); asm.emitBCCTR(); } else { asm.emitCMPI(S1, sigIds[middle]); if (low < middle) { asm.emitShortBC(LT, 0, bcIndices[(low + middle - 1) / 2]); } if (middle < high) { asm.emitShortBC(GT, 0, bcIndices[(middle + 1 + high) / 2]); } // invoke the method for middle. RVMMethod target = targets[middle]; if (target.isStatic()) { // an error case. asm.emitLAddrToc(S0, target.getOffset()); } else { asm.emitLAddrOffset(S0, S0, target.getOffset()); } asm.emitMTCTR(S0); asm.emitBCCTR(); // Recurse. if (low < middle) { insertStubCase(asm, sigIds, targets, bcIndices, low, middle - 1); } if (middle < high) { insertStubCase(asm, sigIds, targets, bcIndices, middle + 1, high); } } } }