Implemented codegen improvement to use CLR fault handlers for Java catch all handlers, whenever possible.

This commit is contained in:
jfrijters 2010-06-08 05:25:14 +00:00
Родитель 3ca0627114
Коммит 0ed13314f0
3 изменённых файлов: 254 добавлений и 98 удалений

Просмотреть файл

@ -4620,14 +4620,22 @@ namespace IKVM.Internal
sealed class VerifierTypeWrapper : TypeWrapper sealed class VerifierTypeWrapper : TypeWrapper
{ {
internal static readonly TypeWrapper Invalid = null; internal static readonly TypeWrapper Invalid = null;
internal static readonly TypeWrapper Null = new VerifierTypeWrapper("null", 0, null); internal static readonly TypeWrapper Null = new VerifierTypeWrapper("null", 0, null, null);
internal static readonly TypeWrapper UninitializedThis = new VerifierTypeWrapper("uninitialized-this", 0, null); internal static readonly TypeWrapper UninitializedThis = new VerifierTypeWrapper("uninitialized-this", 0, null, null);
internal static readonly TypeWrapper Unloadable = new UnloadableTypeWrapper("<verifier>"); internal static readonly TypeWrapper Unloadable = new UnloadableTypeWrapper("<verifier>");
internal static readonly TypeWrapper ExtendedFloat = new VerifierTypeWrapper("<extfloat>", 0, null); internal static readonly TypeWrapper ExtendedFloat = new VerifierTypeWrapper("<extfloat>", 0, null, null);
internal static readonly TypeWrapper ExtendedDouble = new VerifierTypeWrapper("<extdouble>", 0, null); internal static readonly TypeWrapper ExtendedDouble = new VerifierTypeWrapper("<extdouble>", 0, null, null);
private int index; private int index;
private TypeWrapper underlyingType; private TypeWrapper underlyingType;
private MethodAnalyzer methodAnalyzer;
#if STUB_GENERATOR
internal class MethodAnalyzer
{
internal void ClearFaultBlockException(int dummy) { }
}
#endif
public override string ToString() public override string ToString()
{ {
@ -4636,7 +4644,12 @@ namespace IKVM.Internal
internal static TypeWrapper MakeNew(TypeWrapper type, int bytecodeIndex) internal static TypeWrapper MakeNew(TypeWrapper type, int bytecodeIndex)
{ {
return new VerifierTypeWrapper("new", bytecodeIndex, type); return new VerifierTypeWrapper("new", bytecodeIndex, type, null);
}
internal static TypeWrapper MakeFaultBlockException(MethodAnalyzer ma, int handlerIndex)
{
return new VerifierTypeWrapper("<fault>", handlerIndex, null, ma);
} }
// NOTE the "this" type is special, it can only exist in local[0] and on the stack // NOTE the "this" type is special, it can only exist in local[0] and on the stack
@ -4646,7 +4659,7 @@ namespace IKVM.Internal
// stack (using ldarg_0). // stack (using ldarg_0).
internal static TypeWrapper MakeThis(TypeWrapper type) internal static TypeWrapper MakeThis(TypeWrapper type)
{ {
return new VerifierTypeWrapper("this", 0, type); return new VerifierTypeWrapper("this", 0, type, null);
} }
internal static bool IsNew(TypeWrapper w) internal static bool IsNew(TypeWrapper w)
@ -4654,6 +4667,11 @@ namespace IKVM.Internal
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "new"); return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "new");
} }
internal static bool IsFaultBlockException(TypeWrapper w)
{
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "<fault>");
}
internal static bool IsNullOrUnloadable(TypeWrapper w) internal static bool IsNullOrUnloadable(TypeWrapper w)
{ {
return w == Null || w.IsUnloadable; return w == Null || w.IsUnloadable;
@ -4664,6 +4682,12 @@ namespace IKVM.Internal
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "this"); return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "this");
} }
internal static void ClearFaultBlockException(TypeWrapper w)
{
VerifierTypeWrapper vtw = (VerifierTypeWrapper)w;
vtw.methodAnalyzer.ClearFaultBlockException(vtw.Index);
}
internal int Index internal int Index
{ {
get get
@ -4680,11 +4704,12 @@ namespace IKVM.Internal
} }
} }
private VerifierTypeWrapper(string name, int index, TypeWrapper underlyingType) private VerifierTypeWrapper(string name, int index, TypeWrapper underlyingType, MethodAnalyzer methodAnalyzer)
: base(TypeWrapper.VerifierTypeModifiersHack, name, null) : base(TypeWrapper.VerifierTypeModifiersHack, name, null)
{ {
this.index = index; this.index = index;
this.underlyingType = underlyingType; this.underlyingType = underlyingType;
this.methodAnalyzer = methodAnalyzer;
} }
internal override ClassLoaderWrapper GetClassLoader() internal override ClassLoaderWrapper GetClassLoader()

Просмотреть файл

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2002-2009 Jeroen Frijters Copyright (C) 2002-2010 Jeroen Frijters
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -173,8 +173,6 @@ sealed class Compiler
private readonly ClassFile.Method m; private readonly ClassFile.Method m;
private readonly CodeEmitter ilGenerator; private readonly CodeEmitter ilGenerator;
private readonly MethodAnalyzer ma; private readonly MethodAnalyzer ma;
private readonly ClassFile.Method.InstructionFlags[] flags;
private readonly ExceptionTableEntry[] exceptions;
private readonly ISymbolDocumentWriter symboldocument; private readonly ISymbolDocumentWriter symboldocument;
private readonly LineNumberTableAttribute.LineNumberWriter lineNumbers; private readonly LineNumberTableAttribute.LineNumberWriter lineNumbers;
private bool nonleaf; private bool nonleaf;
@ -300,9 +298,6 @@ sealed class Compiler
} }
} }
flags = ma.ComputePartialReachability(0, m.ExceptionTable);
exceptions = MethodAnalyzer.UntangleExceptionBlocks(classFile, m.Instructions, flags, m.ExceptionTable);
// if we're emitting debugging information, we need to use scopes for local variables // if we're emitting debugging information, we need to use scopes for local variables
if(debug) if(debug)
{ {
@ -414,6 +409,7 @@ sealed class Compiler
New, New,
This, This,
UnitializedThis, UnitializedThis,
FaultBlockException,
Other Other
} }
private Compiler compiler; private Compiler compiler;
@ -466,6 +462,10 @@ sealed class Compiler
// uninitialized references cannot be stored in a local, but we can reload them // uninitialized references cannot be stored in a local, but we can reload them
types[i] = StackType.UnitializedThis; types[i] = StackType.UnitializedThis;
} }
else if (VerifierTypeWrapper.IsFaultBlockException(type))
{
types[i] = StackType.FaultBlockException;
}
else else
{ {
types[i] = StackType.Other; types[i] = StackType.Other;
@ -481,7 +481,8 @@ sealed class Compiler
compiler.ilGenerator.Emit(OpCodes.Ldnull); compiler.ilGenerator.Emit(OpCodes.Ldnull);
break; break;
case StackType.New: case StackType.New:
// new objects aren't really there on the stack case StackType.FaultBlockException:
// objects aren't really there on the stack
break; break;
case StackType.This: case StackType.This:
case StackType.UnitializedThis: case StackType.UnitializedThis:
@ -505,7 +506,8 @@ sealed class Compiler
compiler.ilGenerator.LazyEmitPop(); compiler.ilGenerator.LazyEmitPop();
break; break;
case StackType.New: case StackType.New:
// new objects aren't really there on the stack case StackType.FaultBlockException:
// objects aren't really there on the stack
break; break;
case StackType.Other: case StackType.Other:
compiler.ilGenerator.Emit(OpCodes.Stloc, locals[i]); compiler.ilGenerator.Emit(OpCodes.Stloc, locals[i]);
@ -615,7 +617,7 @@ sealed class Compiler
ilGenerator.Emit(OpCodes.Call, monitorEnterMethod); ilGenerator.Emit(OpCodes.Call, monitorEnterMethod);
ilGenerator.BeginExceptionBlock(); ilGenerator.BeginExceptionBlock();
Block b = new Block(c, 0, int.MaxValue, -1, new List<object>(), true); Block b = new Block(c, 0, int.MaxValue, -1, new List<object>(), true);
c.Compile(b); c.Compile(b, c.ma.ComputePartialReachability(0, true));
b.Leave(); b.Leave();
ilGenerator.BeginFinallyBlock(); ilGenerator.BeginFinallyBlock();
ilGenerator.Emit(OpCodes.Ldloc, monitor); ilGenerator.Emit(OpCodes.Ldloc, monitor);
@ -626,14 +628,15 @@ sealed class Compiler
else else
{ {
Block b = new Block(c, 0, int.MaxValue, -1, null, false); Block b = new Block(c, 0, int.MaxValue, -1, null, false);
c.Compile(b); c.Compile(b, c.ma.ComputePartialReachability(0, true));
b.Leave(); b.Leave();
} }
if(c.lineNumbers != null) if(c.lineNumbers != null)
{ {
InstructionFlags[] flags = c.ma.ComputePartialReachability(0, false);
for(int i = 0; i < m.Instructions.Length; i++) for(int i = 0; i < m.Instructions.Length; i++)
{ {
if((c.flags[i] & InstructionFlags.Reachable) == 0) if((flags[i] & InstructionFlags.Reachable) == 0)
{ {
// skip unreachable instructions // skip unreachable instructions
} }
@ -885,31 +888,9 @@ sealed class Compiler
} }
} }
private bool IsGuardedBlock(Stack<Block> blockStack, int instructionIndex, int instructionCount) private void Compile(Block block, InstructionFlags[] flags)
{
int start = instructionIndex;
int end = instructionIndex + instructionCount;
for(int i = 0; i < exceptions.Length; i++)
{
ExceptionTableEntry e = exceptions[i];
if(e.endIndex > start && e.startIndex < end)
{
foreach(Block block in blockStack)
{
if(block.ExceptionIndex == i)
{
goto next;
}
}
return true;
}
next:;
}
return false;
}
private void Compile(Block block)
{ {
ExceptionTableEntry[] exceptions = ma.GetExceptionTableFor(flags);
int exceptionIndex = 0; int exceptionIndex = 0;
Instruction[] code = m.Instructions; Instruction[] code = m.Instructions;
Stack<Block> blockStack = new Stack<Block>(); Stack<Block> blockStack = new Stack<Block>();
@ -948,32 +929,10 @@ sealed class Compiler
int handlerIndex = exc.handlerIndex; int handlerIndex = exc.handlerIndex;
if(exc.catch_type == 0 if(exc.catch_type == 0 && VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(handlerIndex, 0)))
&& handlerIndex + 2 < m.Instructions.Length
&& m.Instructions[handlerIndex].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[handlerIndex + 1].NormalizedOpCode == NormalizedByteCode.__monitorexit
&& m.Instructions[handlerIndex + 2].NormalizedOpCode == NormalizedByteCode.__athrow
&& !IsGuardedBlock(blockStack, handlerIndex, 3))
{ {
// this is the Jikes & Eclipse Java Compiler synchronization block exit
ilGenerator.BeginFaultBlock(); ilGenerator.BeginFaultBlock();
LoadLocal(handlerIndex); Compile(new Block(this, 0, block.EndIndex, exceptionIndex, null, false), ma.ComputePartialReachability(handlerIndex, true));
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
ilGenerator.EndExceptionBlockNoFallThrough();
}
else if(exc.catch_type == 0
&& handlerIndex + 3 < m.Instructions.Length
&& m.Instructions[handlerIndex].NormalizedOpCode == NormalizedByteCode.__astore
&& m.Instructions[handlerIndex + 1].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[handlerIndex + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit
&& m.Instructions[handlerIndex + 3].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[handlerIndex + 4].NormalizedOpCode == NormalizedByteCode.__athrow
&& !IsGuardedBlock(blockStack, handlerIndex, 5))
{
// this is the javac synchronization block exit
ilGenerator.BeginFaultBlock();
LoadLocal(handlerIndex + 1);
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
ilGenerator.EndExceptionBlockNoFallThrough(); ilGenerator.EndExceptionBlockNoFallThrough();
} }
else else
@ -1802,6 +1761,10 @@ sealed class Compiler
// so we don't have to worry about that. // so we don't have to worry about that.
ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldarg_0);
} }
else if (VerifierTypeWrapper.IsFaultBlockException(type))
{
// not really there
}
else else
{ {
LocalVar v = LoadLocal(i); LocalVar v = LoadLocal(i);
@ -1827,6 +1790,10 @@ sealed class Compiler
// the unitialized ref is loaded we redirect to the this reference // the unitialized ref is loaded we redirect to the this reference
ilGenerator.LazyEmitPop(); ilGenerator.LazyEmitPop();
} }
else if(VerifierTypeWrapper.IsFaultBlockException(type))
{
// not really there
}
else else
{ {
StoreLocal(i); StoreLocal(i);
@ -1866,7 +1833,7 @@ sealed class Compiler
ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name); ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name);
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewCheckOnly); ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewCheckOnly);
} }
else if(wrapper != clazz && RequiresExplicitClassInit(wrapper, i + 1)) else if(wrapper != clazz && RequiresExplicitClassInit(wrapper, i + 1, flags))
{ {
// trigger cctor (as the spec requires) // trigger cctor (as the spec requires)
wrapper.EmitRunClassConstructor(ilGenerator); wrapper.EmitRunClassConstructor(ilGenerator);
@ -2572,12 +2539,19 @@ sealed class Compiler
ilGenerator.Emit(OpCodes.Throw); ilGenerator.Emit(OpCodes.Throw);
break; break;
case NormalizedByteCode.__athrow: case NormalizedByteCode.__athrow:
if(ma.GetRawStackTypeWrapper(i, 0).IsUnloadable) if (VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(i, 0)))
{
ilGenerator.Emit(OpCodes.Endfinally);
}
else
{
if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable)
{ {
ilGenerator.Emit(OpCodes.Castclass, Types.Exception); ilGenerator.Emit(OpCodes.Castclass, Types.Exception);
} }
ilGenerator.Emit(OpCodes.Call, unmapExceptionMethod); ilGenerator.Emit(OpCodes.Call, unmapExceptionMethod);
ilGenerator.Emit(OpCodes.Throw); ilGenerator.Emit(OpCodes.Throw);
}
break; break;
case NormalizedByteCode.__tableswitch: case NormalizedByteCode.__tableswitch:
{ {
@ -2736,7 +2710,7 @@ sealed class Compiler
} }
} }
private bool RequiresExplicitClassInit(TypeWrapper tw, int index) private bool RequiresExplicitClassInit(TypeWrapper tw, int index, InstructionFlags[] flags)
{ {
ClassFile.Method.Instruction[] code = m.Instructions; ClassFile.Method.Instruction[] code = m.Instructions;
for (; index < code.Length; index++) for (; index < code.Length; index++)

Просмотреть файл

@ -307,6 +307,16 @@ class InstructionState
{ {
return VerifierTypeWrapper.Invalid; return VerifierTypeWrapper.Invalid;
} }
if(VerifierTypeWrapper.IsFaultBlockException(type1))
{
VerifierTypeWrapper.ClearFaultBlockException(type1);
return FindCommonBaseType(CoreClasses.java.lang.Throwable.Wrapper, type2);
}
if(VerifierTypeWrapper.IsFaultBlockException(type2))
{
VerifierTypeWrapper.ClearFaultBlockException(type2);
return FindCommonBaseType(type1, CoreClasses.java.lang.Throwable.Wrapper);
}
if(type1.IsPrimitive || type2.IsPrimitive) if(type1.IsPrimitive || type2.IsPrimitive)
{ {
return VerifierTypeWrapper.Invalid; return VerifierTypeWrapper.Invalid;
@ -677,6 +687,11 @@ class InstructionState
} }
} }
internal TypeWrapper PopFaultBlockException()
{
return stack[--stackSize];
}
internal TypeWrapper PopAnyType() internal TypeWrapper PopAnyType()
{ {
if(stackSize == 0) if(stackSize == 0)
@ -692,6 +707,11 @@ class InstructionState
{ {
type = ((VerifierTypeWrapper)type).UnderlyingType; type = ((VerifierTypeWrapper)type).UnderlyingType;
} }
if(VerifierTypeWrapper.IsFaultBlockException(type))
{
VerifierTypeWrapper.ClearFaultBlockException(type);
type = CoreClasses.java.lang.Throwable.Wrapper;
}
return type; return type;
} }
@ -878,6 +898,16 @@ class InstructionState
} }
} }
} }
internal void ClearFaultBlockException()
{
if(VerifierTypeWrapper.IsFaultBlockException(stack[0]))
{
StackCopyOnWrite();
changed = true;
stack[0] = CoreClasses.java.lang.Throwable.Wrapper;
}
}
} }
struct StackState struct StackState
@ -916,6 +946,11 @@ struct StackState
{ {
type = ((VerifierTypeWrapper)type).UnderlyingType; type = ((VerifierTypeWrapper)type).UnderlyingType;
} }
if(VerifierTypeWrapper.IsFaultBlockException(type))
{
VerifierTypeWrapper.ClearFaultBlockException(type);
type = CoreClasses.java.lang.Throwable.Wrapper;
}
return type; return type;
} }
@ -1118,6 +1153,7 @@ class MethodAnalyzer
private LocalVar[/*instructionIndex*/][/*localIndex*/] invokespecialLocalVars; private LocalVar[/*instructionIndex*/][/*localIndex*/] invokespecialLocalVars;
private LocalVar[/*index*/] allLocalVars; private LocalVar[/*index*/] allLocalVars;
private List<string> errorMessages; private List<string> errorMessages;
private ExceptionTableEntry[] exceptions;
static MethodAnalyzer() static MethodAnalyzer()
{ {
@ -1147,6 +1183,7 @@ class MethodAnalyzer
// HACK because types have to have identity, the new types are cached here // HACK because types have to have identity, the new types are cached here
Dictionary<int, TypeWrapper> newTypes = new Dictionary<int,TypeWrapper>(); Dictionary<int, TypeWrapper> newTypes = new Dictionary<int,TypeWrapper>();
Dictionary<int, TypeWrapper> faultTypes = new Dictionary<int, TypeWrapper>();
try try
{ {
@ -1205,15 +1242,21 @@ class MethodAnalyzer
firstNonArgLocalIndex++; firstNonArgLocalIndex++;
} }
} }
AnalyzeTypeFlow(wrapper, thisType, mw, localStoreReaders, newTypes); AnalyzeTypeFlow(wrapper, thisType, mw, localStoreReaders, newTypes, faultTypes);
exceptions = UntangleExceptionBlocks(classFile, method.ExceptionTable);
OptimizationPass(wrapper, classLoader); OptimizationPass(wrapper, classLoader);
FinalCodePatchup(wrapper, mw); FinalCodePatchup(wrapper, mw);
if (AnalyzePotentialFaultBlocks())
{
AnalyzeTypeFlow(wrapper, thisType, mw, localStoreReaders, newTypes, faultTypes);
}
AnalyzeLocalVariables(localStoreReaders, classLoader); AnalyzeLocalVariables(localStoreReaders, classLoader);
ComputePartialReachability(0, true);
} }
private void AnalyzeTypeFlow(TypeWrapper wrapper, TypeWrapper thisType, MethodWrapper mw, Dictionary<int, string>[] localStoreReaders, Dictionary<int, TypeWrapper> newTypes) private void AnalyzeTypeFlow(TypeWrapper wrapper, TypeWrapper thisType, MethodWrapper mw, Dictionary<int, string>[] localStoreReaders, Dictionary<int, TypeWrapper> newTypes, Dictionary<int, TypeWrapper> faultTypes)
{ {
InstructionState s = state[0].Copy(); InstructionState s = new InstructionState(method.MaxLocals, method.MaxStack);
bool done = false; bool done = false;
ClassFile.Method.Instruction[] instructions = method.Instructions; ClassFile.Method.Instruction[] instructions = method.Instructions;
while(!done) while(!done)
@ -1233,11 +1276,18 @@ class MethodAnalyzer
{ {
if(method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex) if(method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex)
{ {
int idx = method.ExceptionTable[j].handlerIndex;
InstructionState ex = state[i].CopyLocals(); InstructionState ex = state[i].CopyLocals();
int catch_type = method.ExceptionTable[j].catch_type; int catch_type = method.ExceptionTable[j].catch_type;
if(catch_type == 0) if(catch_type == 0)
{ {
ex.PushType(CoreClasses.java.lang.Throwable.Wrapper); TypeWrapper tw;
if (!faultTypes.TryGetValue(idx, out tw))
{
tw = VerifierTypeWrapper.MakeFaultBlockException(this, idx);
faultTypes.Add(idx, tw);
}
ex.PushType(tw);
} }
else else
{ {
@ -1245,7 +1295,6 @@ class MethodAnalyzer
// Throwable as the type and recording a loader constraint // Throwable as the type and recording a loader constraint
ex.PushType(GetConstantPoolClassType(catch_type)); ex.PushType(GetConstantPoolClassType(catch_type));
} }
int idx = method.ExceptionTable[j].handlerIndex;
state[idx] += ex; state[idx] += ex;
} }
} }
@ -1265,6 +1314,11 @@ class MethodAnalyzer
} }
case NormalizedByteCode.__astore: case NormalizedByteCode.__astore:
{ {
if(VerifierTypeWrapper.IsFaultBlockException(s.PeekType()))
{
s.SetLocalType(instr.NormalizedArg1, s.PopFaultBlockException(), i);
break;
}
// NOTE since the reference can be uninitialized, we cannot use PopObjectType // NOTE since the reference can be uninitialized, we cannot use PopObjectType
TypeWrapper type = s.PopType(); TypeWrapper type = s.PopType();
if(type.IsPrimitive) if(type.IsPrimitive)
@ -2058,7 +2112,14 @@ class MethodAnalyzer
s.GetLocalInt(instr.Arg1, ref localStoreReaders[i]); s.GetLocalInt(instr.Arg1, ref localStoreReaders[i]);
break; break;
case NormalizedByteCode.__athrow: case NormalizedByteCode.__athrow:
if (VerifierTypeWrapper.IsFaultBlockException(s.PeekType()))
{
s.PopFaultBlockException();
}
else
{
s.PopObjectType(CoreClasses.java.lang.Throwable.Wrapper); s.PopObjectType(CoreClasses.java.lang.Throwable.Wrapper);
}
break; break;
case NormalizedByteCode.__tableswitch: case NormalizedByteCode.__tableswitch:
case NormalizedByteCode.__lookupswitch: case NormalizedByteCode.__lookupswitch:
@ -2229,7 +2290,7 @@ class MethodAnalyzer
if (assertionsDisabled != null) if (assertionsDisabled != null)
{ {
// compute branch targets // compute branch targets
InstructionFlags[] flags = ComputePartialReachability(0, method.ExceptionTable); InstructionFlags[] flags = ComputePartialReachability(0, false);
ClassFile.Method.Instruction[] instructions = method.Instructions; ClassFile.Method.Instruction[] instructions = method.Instructions;
for (int i = 0; i < instructions.Length; i++) for (int i = 0; i < instructions.Length; i++)
{ {
@ -2465,7 +2526,7 @@ class MethodAnalyzer
} }
} }
internal InstructionFlags[] ComputePartialReachability(int initialInstructionIndex, ClassFile.Method.ExceptionTableEntry[] exceptionTable) internal InstructionFlags[] ComputePartialReachability(int initialInstructionIndex, bool skipFaultBlocks)
{ {
ClassFile.Method.Instruction[] instructions = method.Instructions; ClassFile.Method.Instruction[] instructions = method.Instructions;
InstructionFlags[] flags = new InstructionFlags[instructions.Length]; InstructionFlags[] flags = new InstructionFlags[instructions.Length];
@ -2481,14 +2542,17 @@ class MethodAnalyzer
done = false; done = false;
flags[i] |= InstructionFlags.Processed; flags[i] |= InstructionFlags.Processed;
// mark the exception handlers reachable from this instruction // mark the exception handlers reachable from this instruction
for (int j = 0; j < exceptionTable.Length; j++) for (int j = 0; j < exceptions.Length; j++)
{ {
if (exceptionTable[j].startIndex <= i && i < exceptionTable[j].endIndex) if (exceptions[j].startIndex <= i && i < exceptions[j].endIndex)
{
int idx = exceptions[j].handlerIndex;
if (!skipFaultBlocks || !VerifierTypeWrapper.IsFaultBlockException(state[idx].GetStackByIndex(0)))
{ {
int idx = exceptionTable[j].handlerIndex;
flags[idx] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget; flags[idx] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
} }
} }
}
// mark the successor instructions // mark the successor instructions
switch (instructions[i].NormalizedOpCode) switch (instructions[i].NormalizedOpCode)
{ {
@ -2531,6 +2595,7 @@ class MethodAnalyzer
case NormalizedByteCode.__areturn: case NormalizedByteCode.__areturn:
case NormalizedByteCode.__return: case NormalizedByteCode.__return:
case NormalizedByteCode.__athrow: case NormalizedByteCode.__athrow:
case NormalizedByteCode.__athrow_no_unmap:
case NormalizedByteCode.__static_error: case NormalizedByteCode.__static_error:
break; break;
default: default:
@ -2559,7 +2624,7 @@ class MethodAnalyzer
Dictionary<LocalVar, LocalVar> forwarders = new Dictionary<LocalVar,LocalVar>(); Dictionary<LocalVar, LocalVar> forwarders = new Dictionary<LocalVar,LocalVar>();
if(classLoader.EmitDebugInfo) if(classLoader.EmitDebugInfo)
{ {
InstructionFlags[] flags = ComputePartialReachability(0, method.ExceptionTable); InstructionFlags[] flags = ComputePartialReachability(0, false);
// if we're emitting debug info, we need to keep dead stores as well... // if we're emitting debug info, we need to keep dead stores as well...
for(int i = 0; i < instructions.Length; i++) for(int i = 0; i < instructions.Length; i++)
{ {
@ -2668,8 +2733,9 @@ class MethodAnalyzer
this.allLocalVars = locals.ToArray(); this.allLocalVars = locals.ToArray();
} }
internal static ExceptionTableEntry[] UntangleExceptionBlocks(ClassFile classFile, ClassFile.Method.Instruction[] instructions, InstructionFlags[] flags, ExceptionTableEntry[] exceptionTable) private ExceptionTableEntry[] UntangleExceptionBlocks(ClassFile classFile, ExceptionTableEntry[] exceptionTable)
{ {
ClassFile.Method.Instruction[] instructions = method.Instructions;
List<ExceptionTableEntry> ar = new List<ExceptionTableEntry>(exceptionTable); List<ExceptionTableEntry> ar = new List<ExceptionTableEntry>(exceptionTable);
// This optimization removes the recursive exception handlers that Java compiler place around // This optimization removes the recursive exception handlers that Java compiler place around
@ -2711,6 +2777,14 @@ class MethodAnalyzer
ar.RemoveAt(i); ar.RemoveAt(i);
i--; i--;
} }
else if (index + 1 < instructions.Length
&& ei.endIndex == index + 1
&& instructions[index].NormalizedOpCode == NormalizedByteCode.__astore)
{
// this is the finally guard that javac produces
ar.RemoveAt(i);
i--;
}
} }
} }
@ -2916,18 +2990,6 @@ class MethodAnalyzer
next: ; next: ;
} }
// remove unreachable exception handlers (because the code gen depends on that)
for (int i = 0; i < ar.Count; i++)
{
// if the first instruction is unreachable, the entire block is unreachable,
// because you can't jump into a block (we've just split the blocks to ensure that)
if ((flags[ar[i].startIndex] & InstructionFlags.Reachable) == 0)
{
ar.RemoveAt(i);
i--;
}
}
ExceptionTableEntry[] exceptions = ar.ToArray(); ExceptionTableEntry[] exceptions = ar.ToArray();
Array.Sort(exceptions, new ExceptionSorter()); Array.Sort(exceptions, new ExceptionSorter());
@ -2957,6 +3019,79 @@ class MethodAnalyzer
return exceptions; return exceptions;
} }
private bool AnalyzePotentialFaultBlocks()
{
ClassFile.Method.Instruction[] code = method.Instructions;
bool changed = false;
bool done = false;
while (!done)
{
done = true;
Stack<ExceptionTableEntry> stack = new Stack<ExceptionTableEntry>();
ExceptionTableEntry current = new ExceptionTableEntry(0, code.Length, -1, ushort.MaxValue, -1);
stack.Push(current);
for (int i = 0; i < exceptions.Length; i++)
{
while (exceptions[i].startIndex >= current.endIndex)
{
current = stack.Pop();
}
Debug.Assert(exceptions[i].startIndex >= current.startIndex && exceptions[i].endIndex <= current.endIndex);
if (exceptions[i].catch_type == 0
&& state[exceptions[i].handlerIndex] != null
&& VerifierTypeWrapper.IsFaultBlockException(GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0)))
{
InstructionFlags[] flags = ComputePartialReachability(exceptions[i].handlerIndex, true);
for (int j = 0; j < code.Length; j++)
{
if ((flags[j] & InstructionFlags.Reachable) != 0)
{
switch (code[j].NormalizedOpCode)
{
case NormalizedByteCode.__return:
case NormalizedByteCode.__areturn:
case NormalizedByteCode.__ireturn:
case NormalizedByteCode.__lreturn:
case NormalizedByteCode.__freturn:
case NormalizedByteCode.__dreturn:
goto not_fault_block;
case NormalizedByteCode.__athrow:
for (int k = i + 1; k < exceptions.Length; k++)
{
if (exceptions[k].startIndex <= j && j < exceptions[k].endIndex)
{
goto not_fault_block;
}
}
break;
}
if (j < current.startIndex || j >= current.endIndex)
{
goto not_fault_block;
}
else if (exceptions[i].startIndex <= j && j < exceptions[i].endIndex)
{
goto not_fault_block;
}
else
{
continue;
}
not_fault_block:
VerifierTypeWrapper.ClearFaultBlockException(GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0));
done = false;
changed = true;
break;
}
}
}
stack.Push(current);
current = exceptions[i];
}
}
return changed;
}
private void SetHardError(ref ClassFile.Method.Instruction instruction, HardError hardError, string message, params object[] args) private void SetHardError(ref ClassFile.Method.Instruction instruction, HardError hardError, string message, params object[] args)
{ {
string text = string.Format(message, args); string text = string.Format(message, args);
@ -3652,4 +3787,26 @@ class MethodAnalyzer
{ {
return allLocalVars; return allLocalVars;
} }
internal void ClearFaultBlockException(int instructionIndex)
{
Debug.Assert(state[instructionIndex].GetStackHeight() == 1);
state[instructionIndex].ClearFaultBlockException();
}
internal ExceptionTableEntry[] GetExceptionTableFor(InstructionFlags[] flags)
{
List<ExceptionTableEntry> list = new List<ExceptionTableEntry>();
// return only reachable exception handlers (because the code gen depends on that)
for (int i = 0; i < exceptions.Length; i++)
{
// if the first instruction is unreachable, the entire block is unreachable,
// because you can't jump into a block (we've just split the blocks to ensure that)
if ((flags[exceptions[i].startIndex] & InstructionFlags.Reachable) != 0)
{
list.Add(exceptions[i]);
}
}
return list.ToArray();
}
} }