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.compilers.opt.runtimesupport;
014    
015    import java.util.Enumeration;
016    
017    import org.jikesrvm.VM;
018    import org.jikesrvm.compilers.common.ExceptionTable;
019    import org.jikesrvm.compilers.opt.ir.BasicBlock;
020    import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock;
021    import org.jikesrvm.compilers.opt.ir.IR;
022    import org.jikesrvm.compilers.opt.ir.operand.TypeOperand;
023    
024    /**
025     * Encoding of try ranges in the final machinecode and the
026     * corresponding exception type and catch block start.
027     */
028    final class OptExceptionTable extends ExceptionTable {
029    
030      /**
031       * Encode an exception table
032       * @param ir the IR to encode the exception table for
033       * @return the encoded exception table
034       */
035      static int[] encode(IR ir) {
036        int index = 0;
037        int currStartOff, currEndOff;
038        int tableSize = countExceptionTableSize(ir);
039        int[] eTable = new int[tableSize * 4];
040    
041        // For each basic block
042        //   See if it has code associated with it and if it has
043        //   any reachable exception handlers.
044        //   When such a block is found, check the blocks that follow
045        //   it in code order to see if this block has the same
046        //   Bag of exceptionHandlers as any of its immediate successors.
047        //   If so the try region can be expanded to include those
048        //   successors. Stop checking successors as soon as a non-match
049        //   is found, or a block that doesn't have handlers is found.
050        //   Successors that don't have any code associated with them can
051        //   be ignored.
052        //   If blocks were joined together then when adding the
053        //   entries to the eTable it is important to not restrict the
054        //   entries to reachable handlers; as the first block may only
055        //   throw a subset of the exception types represented by the Bag
056        for (BasicBlock bblock = ir.firstBasicBlockInCodeOrder(); bblock != null;) {
057          // Iteration is explicit in loop
058    
059          int startOff = bblock.firstInstruction().getmcOffset();
060          int endOff = bblock.lastInstruction().getmcOffset();
061          if (endOff > startOff) {
062            if (!bblock.hasExceptionHandlers()) {
063              bblock = bblock.nextBasicBlockInCodeOrder();
064              continue;
065            }
066    
067            BasicBlock followonBB;
068            Enumeration<BasicBlock> reachBBe, e;
069            boolean joinedBlocks;
070    
071            // First make sure at least one of the exception handlers
072            // is reachable from this block
073            reachBBe = bblock.getReachableExceptionHandlers();
074            if (!reachBBe.hasMoreElements()) {
075              bblock = bblock.nextBasicBlockInCodeOrder();
076              continue;
077            }
078    
079            currStartOff = startOff;
080            currEndOff = endOff;
081            joinedBlocks = false;
082    
083            for (followonBB = bblock.nextBasicBlockInCodeOrder(); followonBB != null; followonBB =
084                followonBB.nextBasicBlockInCodeOrder()) {
085              int fStartOff = followonBB.firstInstruction().getmcOffset();
086              int fEndOff = followonBB.lastInstruction().getmcOffset();
087              // See if followon Block has any code
088              if (fEndOff > fStartOff) {
089                // See if followon Block has matching handler block bag
090                if (followonBB.hasExceptionHandlers() && bblock.isExceptionHandlerEquivalent(followonBB)) {
091                  currEndOff = fEndOff;
092                  joinedBlocks = true;
093                } else {
094                  // Can't join any more blocks together
095                  break;
096                }
097              }
098            }
099            // found all the matching followon blocks
100            // Now fill in the eTable with the handlers
101            if (joinedBlocks) {
102              e = bblock.getExceptionHandlers();
103            } else {
104              e = reachBBe;
105            }
106    
107            while (e.hasMoreElements()) {
108              ExceptionHandlerBasicBlock eBlock = (ExceptionHandlerBasicBlock) e.nextElement();
109              for (java.util.Enumeration<TypeOperand> ets = eBlock.getExceptionTypes(); ets.hasMoreElements();) {
110                TypeOperand type = ets.nextElement();
111                int catchOffset = eBlock.firstInstruction().getmcOffset();
112                eTable[index + TRY_START] = currStartOff;
113                eTable[index + TRY_END] = currEndOff;
114                eTable[index + CATCH_START] = catchOffset;
115                try {
116                  eTable[index + EX_TYPE] = type.getTypeRef().resolve().getId();
117                } catch (NoClassDefFoundError except) {
118                  // Yuck.  If this happens beatup Dave and make him do the right
119                  // thing. For now, we are forcing early loading of exception
120                  // types to avoid a bunch of ugly issues in resolving the type
121                  // when delivering the exception.  The problem is that we
122                  // currently can't allow a GC while in the midst of delivering
123                  // an exception and resolving the type reference might entail
124                  // calling arbitrary classloader code.
125                  VM.sysWriteln("Trouble resolving a caught exception at compile time:");
126                  except.printStackTrace(); // sysFail won't print the stack trace
127                  // that lead to the
128                  // NoClassDefFoundError.
129                  VM.sysFail("Unable to resolve caught exception type at compile time");
130                }
131                index += 4;
132              }
133            }
134            bblock = followonBB;
135          } else {
136            // No code in bblock
137            bblock = bblock.nextBasicBlockInCodeOrder();
138          }
139        }
140    
141        if (index != eTable.length) {              // resize array
142          int[] newETable = new int[index];
143          for (int i = 0; i < index; i++) {
144            newETable[i] = eTable[i];
145          }
146          eTable = newETable;
147        }
148        return eTable;
149      }
150    
151      /**
152       * Return an upper bounds on the size of the exception table for an IR.
153       */
154      private static int countExceptionTableSize(IR ir) {
155        int tSize = 0;
156        for (BasicBlock bblock = ir.firstBasicBlockInCodeOrder(); bblock != null; bblock =
157            bblock.nextBasicBlockInCodeOrder()) {
158          if (bblock.hasExceptionHandlers()) {
159            for (Enumeration<BasicBlock> e = bblock.getExceptionHandlers(); e.hasMoreElements();) {
160              ExceptionHandlerBasicBlock ebb = (ExceptionHandlerBasicBlock) e.nextElement();
161              tSize += ebb.getNumberOfExceptionTableEntries();
162            }
163          }
164        }
165        return tSize;
166      }
167    }
168    
169    
170