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    }