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