001 /*
002 * This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 * This file is licensed to You under the Eclipse Public License (EPL);
005 * You may not use this file except in compliance with the License. You
006 * may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 * See the COPYRIGHT.txt file distributed with this work for information
011 * regarding copyright ownership.
012 */
013 package org.jikesrvm.ia32;
014
015 import org.jikesrvm.ArchitectureSpecific;
016 import org.jikesrvm.VM;
017 import org.jikesrvm.classloader.RVMMethod;
018 import org.jikesrvm.compilers.common.assembler.ia32.Assembler;
019 import org.jikesrvm.objectmodel.ObjectModel;
020 import org.jikesrvm.runtime.ArchEntrypoints;
021 import org.jikesrvm.runtime.Magic;
022 import org.vmmagic.unboxed.Offset;
023
024 /**
025 * An interface conflict resolution stub uses a hidden parameter to
026 * distinguish among multiple interface methods of a class that map to
027 * the same slot in the class's IMT. </p>
028 *
029 * <p><STRONG>Assumption:</STRONG>
030 * Register EAX contains the "this" parameter of the
031 * method being called invoked.
032 *
033 * <p><STRONG>Assumption:</STRONG>
034 * Register ECX is available as a scratch register (we need one!)
035 */
036 public abstract class InterfaceMethodConflictResolver implements RegisterConstants {
037
038 // Create a conflict resolution stub for the set of interface method signatures l.
039 //
040 public static ArchitectureSpecific.CodeArray createStub(int[] sigIds, RVMMethod[] targets) {
041 int numEntries = sigIds.length;
042 // (1) Create an assembler.
043 Assembler asm = new ArchitectureSpecific.Assembler(numEntries);
044
045 // (2) signatures must be in ascending order (to build binary search tree).
046 if (VM.VerifyAssertions) {
047 for (int i = 1; i < sigIds.length; i++) {
048 VM._assert(sigIds[i - 1] < sigIds[i]);
049 }
050 }
051
052 // (3) Assign synthetic bytecode numbers to each switch such that we'll generate them
053 // in ascending order. This lets us use the general forward branching mechanisms
054 // of the Assembler.
055 int[] bcIndices = new int[numEntries];
056 assignBytecodeIndices(0, bcIndices, 0, numEntries - 1);
057
058 // (4) Generate the stub.
059 insertStubPrologue(asm);
060 insertStubCase(asm, sigIds, targets, bcIndices, 0, numEntries - 1);
061
062 return asm.getMachineCodes();
063 }
064
065 // Assign ascending bytecode indices to each case (in the order they will be generated)
066 private static int assignBytecodeIndices(int bcIndex, int[] bcIndices, int low, int high) {
067 int middle = (high + low) / 2;
068 bcIndices[middle] = bcIndex++;
069 if (low == middle && middle == high) {
070 return bcIndex;
071 } else {
072 // Recurse.
073 if (low < middle) {
074 bcIndex = assignBytecodeIndices(bcIndex, bcIndices, low, middle - 1);
075 }
076 if (middle < high) {
077 bcIndex = assignBytecodeIndices(bcIndex, bcIndices, middle + 1, high);
078 }
079 return bcIndex;
080 }
081 }
082
083 // Make a stub prologue: get TIB into ECX
084 // factor out to reduce code space in each call.
085 //
086 private static void insertStubPrologue(Assembler asm) {
087 ObjectModel.baselineEmitLoadTIB((ArchitectureSpecific.Assembler) asm, ECX.value(), EAX.value());
088 }
089
090 // Generate a subtree covering from low to high inclusive.
091 private static void insertStubCase(Assembler asm, int[] sigIds, RVMMethod[] targets, int[] bcIndices, int low,
092 int high) {
093 int middle = (high + low) / 2;
094 asm.resolveForwardReferences(bcIndices[middle]);
095 if (low == middle && middle == high) {
096 // a leaf case; can simply invoke the method directly.
097 RVMMethod target = targets[middle];
098 if (target.isStatic()) { // an error case...
099 asm.emitJMP_Abs(Magic.getTocPointer().plus(target.getOffset()));
100 } else {
101 asm.emitJMP_RegDisp(ECX, target.getOffset());
102 }
103 } else {
104 Offset disp = ArchEntrypoints.hiddenSignatureIdField.getOffset();
105 ThreadLocalState.emitCompareFieldWithImm(asm, disp, sigIds[middle]);
106 if (low < middle) {
107 asm.emitJCC_Cond_Label(Assembler.LT, bcIndices[(low + middle - 1) / 2]);
108 }
109 if (middle < high) {
110 asm.emitJCC_Cond_Label(Assembler.GT, bcIndices[(middle + 1 + high) / 2]);
111 }
112 // invoke the method for middle.
113 RVMMethod target = targets[middle];
114 if (target.isStatic()) { // an error case...
115 asm.emitJMP_Abs(Magic.getTocPointer().plus(target.getOffset()));
116 } else {
117 asm.emitJMP_RegDisp(ECX, target.getOffset());
118 }
119 // Recurse.
120 if (low < middle) {
121 insertStubCase(asm, sigIds, targets, bcIndices, low, middle - 1);
122 }
123 if (middle < high) {
124 insertStubCase(asm, sigIds, targets, bcIndices, middle + 1, high);
125 }
126 }
127 }
128 }