/*
* 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.ia32;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.compilers.common.assembler.ia32.VM_Assembler;
import org.jikesrvm.objectmodel.VM_ObjectModel;
import org.jikesrvm.runtime.VM_ArchEntrypoints;
import org.vmmagic.unboxed.Offset;
/**
* An interface conflict resolution stub uses a hidden parameter to
* distinguish among multiple interface methods of a class that map to
* the same slot in the class's IMT. </p>
*
* <p><STRONG>Assumption:</STRONG>
* Register EAX contains the "this" parameter of the
* method being called invoked.
*
* <p><STRONG>Assumption:</STRONG>
* Register ECX is available as a scratch register (we need one!)
*/
public abstract class VM_InterfaceMethodConflictResolver implements VM_RegisterConstants {
// Create a conflict resolution stub for the set of interface method signatures l.
//
public static ArchitectureSpecific.VM_CodeArray createStub(int[] sigIds, VM_Method[] targets) {
int numEntries = sigIds.length;
// (1) Create an assembler.
VM_Assembler asm = new ArchitectureSpecific.VM_Assembler(numEntries);
// (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 VM_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);
return asm.getMachineCodes();
}
// Assign ascending bytecode indices to each case (in the order they will be generated)
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 ECX
// factor out to reduce code space in each call.
//
private static void insertStubPrologue(VM_Assembler asm) {
VM_ObjectModel.baselineEmitLoadTIB((ArchitectureSpecific.VM_Assembler) asm, ECX, EAX, false);
}
// Generate a subtree covering from low to high inclusive.
private static void insertStubCase(VM_Assembler asm, int[] sigIds, VM_Method[] 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.
VM_Method target = targets[middle];
if (target.isStatic()) { // an error case...
VM_ProcessorLocalState.emitMoveFieldToReg(asm, ECX, VM_ArchEntrypoints.jtocField.getOffset());
}
asm.emitJMP_RegDisp(ECX, target.getOffset());
} else {
Offset disp = VM_ArchEntrypoints.hiddenSignatureIdField.getOffset();
VM_ProcessorLocalState.emitCompareFieldWithImm(asm, disp, sigIds[middle]);
if (low < middle) {
asm.emitJCC_Cond_Label(VM_Assembler.LT, bcIndices[(low + middle - 1) / 2]);
}
if (middle < high) {
asm.emitJCC_Cond_Label(VM_Assembler.GT, bcIndices[(middle + 1 + high) / 2]);
}
// invoke the method for middle.
VM_Method target = targets[middle];
if (target.isStatic()) { // an error case...
VM_ProcessorLocalState.emitMoveFieldToReg(asm, ECX, VM_ArchEntrypoints.jtocField.getOffset());
}
asm.emitJMP_RegDisp(ECX, target.getOffset());
// Recurse.
if (low < middle) {
insertStubCase(asm, sigIds, targets, bcIndices, low, middle - 1);
}
if (middle < high) {
insertStubCase(asm, sigIds, targets, bcIndices, middle + 1, high);
}
}
}
}