/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt.runtimesupport;
import java.util.Enumeration;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.common.ExceptionTable;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.operand.TypeOperand;
import org.jikesrvm.compilers.opt.mir2mc.MachineCodeOffsets;
import org.vmmagic.pragma.Uninterruptible;
/**
* Encoding of try ranges in the final machinecode and the
* corresponding exception type and catch block start.
*/
final class OptExceptionTable extends ExceptionTable {
/**
* Marker for catch blocks that have no associated code. This happens
* when the optimizing compiler deems the catch block to be unreachable
* and removes it from the IR.
* <p>
* The only constraint on the concrete value for this marker is that
* it must be negative to ensure that the non-existent catch block
* is never found when exceptions are delivered.
*/
private static final int UNREACHABLE_CATCH_BLOCK = 0xDEADC0DE;
/**
* Encode an exception table
* @param ir the IR to encode the exception table for
* @return the encoded exception table
*/
static int[] encode(IR ir) {
int index = 0;
int currStartOff, currEndOff;
int tableSize = countExceptionTableSize(ir);
int[] eTable = new int[tableSize * 4];
MachineCodeOffsets mcOffsets = ir.MIRInfo.mcOffsets;
// For each basic block
// See if it has code associated with it and if it has
// any reachable exception handlers.
// When such a block is found, check the blocks that follow
// it in code order to see if this block has the same
// Bag of exceptionHandlers as any of its immediate successors.
// If so the try region can be expanded to include those
// successors. Stop checking successors as soon as a non-match
// is found, or a block that doesn't have handlers is found.
// Successors that don't have any code associated with them can
// be ignored.
// If blocks were joined together then when adding the
// entries to the eTable it is important to not restrict the
// entries to reachable handlers; as the first block may only
// throw a subset of the exception types represented by the Bag
for (BasicBlock bblock = ir.firstBasicBlockInCodeOrder(); bblock != null;) {
// Iteration is explicit in loop
int startOff = mcOffsets.getMachineCodeOffset(bblock.firstInstruction());
int endOff = mcOffsets.getMachineCodeOffset(bblock.lastInstruction());
if (endOff > startOff) {
if (!bblock.hasExceptionHandlers()) {
bblock = bblock.nextBasicBlockInCodeOrder();
continue;
}
BasicBlock followonBB;
Enumeration<BasicBlock> reachBBe, e;
boolean joinedBlocks;
// First make sure at least one of the exception handlers
// is reachable from this block
reachBBe = bblock.getReachableExceptionHandlers();
if (!reachBBe.hasMoreElements()) {
bblock = bblock.nextBasicBlockInCodeOrder();
continue;
}
currStartOff = startOff;
currEndOff = endOff;
joinedBlocks = false;
for (followonBB = bblock.nextBasicBlockInCodeOrder(); followonBB != null; followonBB =
followonBB.nextBasicBlockInCodeOrder()) {
int fStartOff = mcOffsets.getMachineCodeOffset(followonBB.firstInstruction());
int fEndOff = mcOffsets.getMachineCodeOffset(followonBB.lastInstruction());
// See if followon Block has any code
if (fEndOff > fStartOff) {
// See if followon Block has matching handler block bag
if (followonBB.hasExceptionHandlers() && bblock.isExceptionHandlerEquivalent(followonBB)) {
currEndOff = fEndOff;
joinedBlocks = true;
} else {
// Can't join any more blocks together
break;
}
}
}
// found all the matching followon blocks
// Now fill in the eTable with the handlers
if (joinedBlocks) {
e = bblock.getExceptionHandlers();
} else {
e = reachBBe;
}
while (e.hasMoreElements()) {
ExceptionHandlerBasicBlock eBlock = (ExceptionHandlerBasicBlock) e.nextElement();
for (java.util.Enumeration<TypeOperand> ets = eBlock.getExceptionTypes(); ets.hasMoreElements();) {
TypeOperand type = ets.nextElement();
Instruction label = eBlock.firstInstruction();
int catchOffset;
if (mcOffsets.lacksMachineCodeOffset(label)) {
// handler block was removed from the IR and is unreachable.
// Make sure that we can recognize this as an error at runtime
// if the catch block is actually reachable.
catchOffset = UNREACHABLE_CATCH_BLOCK;
} else {
catchOffset = mcOffsets.getMachineCodeOffset(label);
}
eTable[index + TRY_START] = currStartOff;
eTable[index + TRY_END] = currEndOff;
eTable[index + CATCH_START] = catchOffset;
try {
eTable[index + EX_TYPE] = type.getTypeRef().resolve().getId();
} catch (NoClassDefFoundError except) {
// For now, we are forcing early loading of exception
// types to avoid a bunch of ugly issues in resolving the type
// when delivering the exception. The problem is that we
// currently can't allow a GC while in the midst of delivering
// an exception and resolving the type reference might entail
// calling arbitrary classloader code.
VM.sysWriteln("Trouble resolving a caught exception at compile time:");
except.printStackTrace(); // sysFail won't print the stack trace
// that lead to the
// NoClassDefFoundError.
VM.sysFail("Unable to resolve caught exception type at compile time");
}
index += 4;
}
}
bblock = followonBB;
} else {
// No code in bblock
bblock = bblock.nextBasicBlockInCodeOrder();
}
}
if (index != eTable.length) { // resize array
int[] newETable = new int[index];
for (int i = 0; i < index; i++) {
newETable[i] = eTable[i];
}
eTable = newETable;
}
return eTable;
}
/**
* @param ir the IR with the exception tables
* @return an upper bound on the size of the exception table for an IR.
*/
private static int countExceptionTableSize(IR ir) {
int tSize = 0;
for (BasicBlock bblock = ir.firstBasicBlockInCodeOrder(); bblock != null; bblock =
bblock.nextBasicBlockInCodeOrder()) {
if (bblock.hasExceptionHandlers()) {
for (Enumeration<BasicBlock> e = bblock.getExceptionHandlers(); e.hasMoreElements();) {
ExceptionHandlerBasicBlock ebb = (ExceptionHandlerBasicBlock) e.nextElement();
tSize += ebb.getNumberOfExceptionTableEntries();
}
}
}
return tSize;
}
@Uninterruptible
static boolean belongsToUnreachableCatchBlock(int catchOffset) {
return catchOffset == UNREACHABLE_CATCH_BLOCK;
}
}