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