Moved line number / sequence point handling into CodeEmitter and made things more consistent.

This commit is contained in:
jfrijters 2010-09-28 08:02:07 +00:00
Родитель c6e3c87af1
Коммит ce884000ce
3 изменённых файлов: 96 добавлений и 92 удалений

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

@ -78,13 +78,12 @@ namespace IKVM.Internal
#endif
private Stack<bool> exceptionStack = new Stack<bool>();
private bool inFinally;
#if STATIC_COMPILER
private IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter linenums;
#endif // STATIC_COMPILER
private Expr[] stackArray = new Expr[8];
private int topOfStack;
private CodeEmitterLabel lazyBranch;
private LocalBuilder[] tempLocals = new LocalBuilder[32];
private ISymbolDocumentWriter symbols;
#if LABELCHECK
private Dictionary<CodeEmitterLabel, System.Diagnostics.StackFrame> labels = new Dictionary<CodeEmitterLabel, System.Diagnostics.StackFrame>();
#endif
@ -150,6 +149,11 @@ namespace IKVM.Internal
return stackArray[topOfStack - 1];
}
internal void DefineSymbolDocument(ModuleBuilder module, string url, Guid language, Guid languageVendor, Guid documentType)
{
symbols = module.DefineDocument(url, language, languageVendor, documentType);
}
internal LocalBuilder UnsafeAllocTempLocal(Type type)
{
int free = -1;
@ -210,7 +214,7 @@ namespace IKVM.Internal
}
}
internal int GetILOffset()
private int GetILOffset()
{
LazyGen();
#if STATIC_COMPILER
@ -560,12 +564,6 @@ namespace IKVM.Internal
loc.Offset = GetILOffset();
}
internal void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn)
{
LazyGen();
ilgen_real.MarkSequencePoint(document, startLine, startColumn, endLine, endColumn);
}
internal void ThrowException(Type excType)
{
LazyGen();
@ -575,16 +573,33 @@ namespace IKVM.Internal
ilgen_real.ThrowException(excType);
}
#if STATIC_COMPILER
internal void SetLineNumber(ushort line)
{
if(linenums == null)
if (symbols != null)
{
linenums = new IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter(32);
LazyGen();
ilgen_real.MarkSequencePoint(symbols, line, 0, line + 1, 0);
// we emit a nop to make sure we always have an instruction associated with the sequence point
Emit(OpCodes.Nop);
}
// we only add a line number mapping if the stack is empty because the CLR JIT only generates native to IL mappings
// for locations where the stack is empty and we don't want to needlessly flush the stack
if (topOfStack == 0)
{
if (linenums == null)
{
linenums = new IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter(32);
}
linenums.AddMapping(GetILOffset(), line);
}
linenums.AddMapping(GetILOffset(), line);
}
internal byte[] GetLineNumberTable()
{
return linenums == null ? null : linenums.ToArray();
}
#if STATIC_COMPILER
internal void EmitLineNumberTable(MethodBase mb)
{
if(linenums != null)

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

@ -3803,26 +3803,26 @@ namespace IKVM.Internal
continue;
}
#endif // STATIC_COMPILER
LineNumberTableAttribute.LineNumberWriter lineNumberTable = null;
bool nonleaf = false;
Compiler.Compile(this, wrapper, methods[i], classFile, m, ilGenerator, ref nonleaf, invokespecialstubcache, ref lineNumberTable);
Compiler.Compile(this, wrapper, methods[i], classFile, m, ilGenerator, ref nonleaf, invokespecialstubcache);
ilGenerator.CheckLabels();
if (nonleaf)
{
mbld.SetImplementationFlags(mbld.GetMethodImplementationFlags() | MethodImplAttributes.NoInlining);
}
if (lineNumberTable != null)
{
#if STATIC_COMPILER
AttributeHelper.SetLineNumberTable(methods[i].GetMethod(), lineNumberTable);
ilGenerator.EmitLineNumberTable(methods[i].GetMethod());
#else // STATIC_COMPILER
byte[] linenumbers = ilGenerator.GetLineNumberTable();
if (linenumbers != null)
{
if (wrapper.lineNumberTables == null)
{
wrapper.lineNumberTables = new byte[methods.Length][];
}
wrapper.lineNumberTables[i] = lineNumberTable.ToArray();
#endif // STATIC_COMPILER
wrapper.lineNumberTables[i] = linenumbers;
}
#endif // STATIC_COMPILER
}
}
}
@ -5031,21 +5031,21 @@ namespace IKVM.Internal
return;
}
#endif
LineNumberTableAttribute.LineNumberWriter lineNumberTable = null;
bool nonLeaf = false;
Compiler.Compile(context, wrapper, methods[methodIndex], classFile, m, ilGenerator, ref nonLeaf, invokespecialstubcache, ref lineNumberTable);
if (lineNumberTable != null)
{
Compiler.Compile(context, wrapper, methods[methodIndex], classFile, m, ilGenerator, ref nonLeaf, invokespecialstubcache);
#if STATIC_COMPILER
AttributeHelper.SetLineNumberTable(methods[methodIndex].GetMethod(), lineNumberTable);
ilGenerator.EmitLineNumberTable(methods[methodIndex].GetMethod());
#else // STATIC_COMPILER
byte[] linenumbers = ilGenerator.GetLineNumberTable();
if (linenumbers != null)
{
if (wrapper.lineNumberTables == null)
{
wrapper.lineNumberTables = new byte[methods.Length][];
}
wrapper.lineNumberTables[methodIndex] = lineNumberTable.ToArray();
#endif // STATIC_COMPILER
wrapper.lineNumberTables[methodIndex] = linenumbers;
}
#endif // STATIC_COMPILER
}
private static bool IsCompatibleArgList(TypeWrapper[] caller, TypeWrapper[] callee)

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

@ -176,13 +176,12 @@ sealed class Compiler
private readonly UntangledExceptionTable exceptions;
private readonly List<string> harderrors;
private readonly LocalVarInfo localVars;
private readonly ISymbolDocumentWriter symboldocument;
private readonly LineNumberTableAttribute.LineNumberWriter lineNumbers;
private bool nonleaf;
private Dictionary<MethodKey, MethodInfo> invokespecialstubcache;
private readonly bool debug;
private readonly bool keepAlive;
private readonly bool strictfp;
private readonly bool emitLineNumbers;
private int[] scopeBegin;
private int[] scopeClose;
#if STATIC_COMPILER
@ -225,7 +224,7 @@ sealed class Compiler
getClassFromTypeHandle.Link();
}
private Compiler(DynamicTypeWrapper.FinishContext context, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ClassLoaderWrapper classLoader, ISymbolDocumentWriter symboldocument, Dictionary<MethodKey, MethodInfo> invokespecialstubcache)
private Compiler(DynamicTypeWrapper.FinishContext context, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ClassLoaderWrapper classLoader, Dictionary<MethodKey, MethodInfo> invokespecialstubcache)
{
this.context = context;
this.clazz = clazz;
@ -233,14 +232,9 @@ sealed class Compiler
this.classFile = classFile;
this.m = m;
this.ilGenerator = ilGenerator;
this.symboldocument = symboldocument;
this.invokespecialstubcache = invokespecialstubcache;
this.debug = classLoader.EmitDebugInfo;
this.strictfp = m.IsStrictfp;
if(m.LineNumberTableAttribute != null && classLoader.EmitStackTraceInfo)
{
this.lineNumbers = new LineNumberTableAttribute.LineNumberWriter(m.LineNumberTableAttribute.Length);
}
if(ReferenceEquals(mw.Name, StringConstants.INIT))
{
MethodWrapper finalize = clazz.GetMethodWrapper(StringConstants.FINALIZE, StringConstants.SIG_VOID, true);
@ -267,6 +261,50 @@ sealed class Compiler
Profiler.Leave("MethodAnalyzer");
}
if (m.LineNumberTableAttribute != null)
{
if (classLoader.EmitDebugInfo)
{
emitLineNumbers = true;
}
else if (classLoader.EmitStackTraceInfo)
{
InstructionFlags[] flags = ComputePartialReachability(0, false);
for (int i = 0; i < m.Instructions.Length; i++)
{
if ((flags[i] & InstructionFlags.Reachable) == 0)
{
// skip unreachable instructions
}
else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getfield
&& VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(i, 0)))
{
// loading a field from the current object cannot throw
}
else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putfield
&& VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(i, 1)))
{
// storing a field in the current object cannot throw
}
else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getstatic
&& classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz)
{
// loading a field from the current class cannot throw
}
else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic
&& classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz)
{
// storing a field to the current class cannot throw
}
else if (ByteCodeMetaData.CanThrowException(m.Instructions[i].NormalizedOpCode))
{
emitLineNumbers = true;
break;
}
}
}
}
TypeWrapper[] args = mw.GetParameters();
LocalVar[] locals = localVars.GetAllLocalVars();
foreach(LocalVar v in locals)
@ -524,10 +562,9 @@ sealed class Compiler
}
}
internal static void Compile(DynamicTypeWrapper.FinishContext context, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ref bool nonleaf, Dictionary<MethodKey, MethodInfo> invokespecialstubcache, ref LineNumberTableAttribute.LineNumberWriter lineNumberTable)
internal static void Compile(DynamicTypeWrapper.FinishContext context, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ref bool nonleaf, Dictionary<MethodKey, MethodInfo> invokespecialstubcache)
{
ClassLoaderWrapper classLoader = clazz.GetClassLoader();
ISymbolDocumentWriter symboldocument = null;
if(classLoader.EmitDebugInfo)
{
string sourcefile = classFile.SourceFileAttribute;
@ -540,7 +577,7 @@ sealed class Compiler
package = index == -1 ? "" : package.Substring(0, index).Replace('.', '/');
sourcefile = new System.IO.FileInfo(classLoader.SourcePath + "/" + package + "/" + sourcefile).FullName;
}
symboldocument = classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineDocument(sourcefile, SymLanguageType.Java, Guid.Empty, SymDocumentType.Text);
ilGenerator.DefineSymbolDocument(classLoader.GetTypeWrapperFactory().ModuleBuilder, sourcefile, SymLanguageType.Java, Guid.Empty, SymDocumentType.Text);
// the very first instruction in the method must have an associated line number, to be able
// to step into the method in Visual Studio .NET
ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute;
@ -558,7 +595,7 @@ sealed class Compiler
}
if(firstLine > 0)
{
ilGenerator.MarkSequencePoint(symboldocument, firstLine, 0, firstLine + 1, 0);
ilGenerator.SetLineNumber((ushort)firstLine);
}
}
}
@ -582,7 +619,7 @@ sealed class Compiler
Profiler.Enter("new Compiler");
try
{
c = new Compiler(context, clazz, mw, classFile, m, ilGenerator, classLoader, symboldocument, invokespecialstubcache);
c = new Compiler(context, clazz, mw, classFile, m, ilGenerator, classLoader, invokespecialstubcache);
}
finally
{
@ -637,42 +674,6 @@ sealed class Compiler
c.Compile(b, c.ComputePartialReachability(0, true));
b.Leave();
}
if(c.lineNumbers != null)
{
InstructionFlags[] flags = c.ComputePartialReachability(0, false);
for(int i = 0; i < m.Instructions.Length; i++)
{
if((flags[i] & InstructionFlags.Reachable) == 0)
{
// skip unreachable instructions
}
else if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getfield
&& VerifierTypeWrapper.IsThis(c.ma.GetRawStackTypeWrapper(i, 0)))
{
// loading a field from the current object cannot throw
}
else if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putfield
&& VerifierTypeWrapper.IsThis(c.ma.GetRawStackTypeWrapper(i, 1)))
{
// storing a field in the current object cannot throw
}
else if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getstatic
&& classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz)
{
// loading a field from the current class cannot throw
}
else if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic
&& classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz)
{
// storing a field to the current class cannot throw
}
else if(ByteCodeMetaData.CanThrowException(m.Instructions[i].NormalizedOpCode))
{
lineNumberTable = c.lineNumbers;
break;
}
}
}
nonleaf = c.nonleaf;
}
finally
@ -1097,26 +1098,14 @@ sealed class Compiler
block.MarkLabel(i);
}
ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute;
if(table != null && (symboldocument != null || lineNumbers != null))
if(emitLineNumbers)
{
for(int j = 0; j < table.Length; j++)
ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute;
for (int j = 0; j < table.Length; j++)
{
if(table[j].start_pc == code[i].PC && table[j].line_number != 0)
{
if(symboldocument != null)
{
ilGenerator.MarkSequencePoint(symboldocument, table[j].line_number, 0, table[j].line_number + 1, 0);
// we emit a nop to make sure we always have an instruction associated with the sequence point
ilGenerator.Emit(OpCodes.Nop);
}
// we only add a line number mapping if the stack is empty for two reasons:
// 1) the CLR JIT only generates native to IL mappings for locations where the stack is empty
// 2) GetILOffset() flushes the lazy emit stack, so if we don't do this check we miss some optimization opportunities
if(lineNumbers != null && ilGenerator.IsStackEmpty)
{
lineNumbers.AddMapping(ilGenerator.GetILOffset(), table[j].line_number);
}
ilGenerator.SetLineNumber(table[j].line_number);
break;
}
}