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}