diff --git a/runtime/CodeEmitter.cs b/runtime/CodeEmitter.cs index fec4a4ff..ffcba91a 100644 --- a/runtime/CodeEmitter.cs +++ b/runtime/CodeEmitter.cs @@ -151,6 +151,13 @@ namespace IKVM.Internal this.data = data; } + internal bool Match(OpCodeWrapper other) + { + return other.pseudo == pseudo + && other.opcode == opcode + && (other.data == data || (data != null && data.Equals(other.data))); + } + internal bool HasLabel { get { return data is CodeEmitterLabel; } @@ -361,6 +368,11 @@ namespace IKVM.Internal codeEmitter.RealEmitPseudoOpCode(ilOffset, pseudo, data); } } + + public override string ToString() + { + return pseudo.ToString() + " " + data; + } } sealed class CalliWrapper @@ -549,12 +561,22 @@ namespace IKVM.Internal { for (int i = 1; i < code.Count; i++) { - if (code[i].pseudo == CodeType.Label - && code[i - 1].opcode == OpCodes.Br - && code[i - 1].MatchLabel(code[i])) + if (code[i].pseudo == CodeType.Label) { - code.RemoveAt(i - 1); - i--; + if (code[i - 1].opcode == OpCodes.Br + && code[i - 1].MatchLabel(code[i])) + { + code.RemoveAt(i - 1); + i--; + } + else if (i >= 2 + && code[i - 1].pseudo == CodeType.LineNumber + && code[i - 2].opcode == OpCodes.Br + && code[i - 2].MatchLabel(code[i])) + { + code.RemoveAt(i - 2); + i--; + } } } } @@ -1053,7 +1075,7 @@ namespace IKVM.Internal } } - private void BubblePopsAndReleaseTempLocals() + private void SortPseudoOpCodes() { for (int i = 0; i < code.Count - 1; i++) { @@ -1066,7 +1088,7 @@ namespace IKVM.Internal OpCodeWrapper temp = code[i]; code[i] = code[i + 1]; code[i + 1] = temp; - i -= 2; + i--; } break; case CodeType.BeginExceptionBlock: @@ -1075,7 +1097,16 @@ namespace IKVM.Internal OpCodeWrapper temp = code[i]; code[i] = code[i + 1]; code[i + 1] = temp; - i -= 2; + i--; + } + break; + case CodeType.Label: + if (code[i + 1].pseudo == CodeType.BeginExceptionBlock) + { + OpCodeWrapper temp = code[i]; + code[i] = code[i + 1]; + code[i + 1] = temp; + i--; } break; } @@ -1273,6 +1304,253 @@ namespace IKVM.Internal } } } + + // TODO can't we incorporate this in the above code? + // remove exception blocks with empty try blocks + // (which can happen if the try block is unreachable) + for (int i = 0; i < code.Count; i++) + { + restart: + if (code[i].pseudo == CodeType.BeginExceptionBlock) + { + for (int k = 0; ; k++) + { + switch (code[i + k].pseudo) + { + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + int depth = 0; + for (int j = i + 1; ; j++) + { + switch (code[j].pseudo) + { + case CodeType.BeginExceptionBlock: + depth++; + break; + case CodeType.EndExceptionBlock: + if (depth == 0) + { + code.RemoveRange(i, (j - i) + 1); + goto restart; + } + depth--; + break; + } + } + case CodeType.OpCode: + goto next; + } + } + } + next: ; + } + } + + private void DeduplicateBranchSourceTargetCode() + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.Label) + { + code[i].Label.Temp = i; + } + } + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Br && code[i].HasLabel) + { + int source = i - 1; + int target = code[i].Label.Temp - 1; + while (source >= 0 && target >= 0) + { + switch (code[source].pseudo) + { + case CodeType.LineNumber: + case CodeType.OpCode: + break; + default: + goto break_while; + } + if (!code[source].Match(code[target])) + { + break; + } + switch (code[source].opcode.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Cond_Branch: + goto break_while; + } + source--; + target--; + } + break_while: ; + source++; + target++; + if (source != i && target > 0 && source != target - 1) + { + // TODO for now we only do this optimization if there happens to be an appriopriate label + if (code[target - 1].pseudo == CodeType.Label) + { + code[source] = new OpCodeWrapper(OpCodes.Br, code[target - 1].Label); + for (int j = source + 1; j < i; j++) + { + // This is to make sure that any line numbers here are removed by DCE. + // It may be better to fix DCE to remove unreachable line numbers. + code[j] = new OpCodeWrapper(OpCodes.Nop, null); + } + } + } + } + } + } + + private void OptimizeStackTransfer() + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Ldloc + && code[i + 1].opcode == OpCodes.Stloc + && code[i + 2].pseudo == CodeType.BeginExceptionBlock + && code[i + 3].opcode == OpCodes.Ldloc && code[i + 3].MatchLocal(code[i + 1]) + && code[i + 4].pseudo == CodeType.ReleaseTempLocal && code[i + 4].MatchLocal(code[i + 3])) + { + code[i + 1] = code[i]; + code[i] = code[i + 2]; + code.RemoveRange(i + 2, 3); + } + } + } + + private void MergeExceptionBlocks() + { + // The first loop will convert all Begin[Exception|Catch|Fault|Finally]Block and EndExceptionBlock + // pseudo opcodes into a cyclic linked list (EndExceptionBlock links back to BeginExceptionBlock) + // to allow for easy traversal in the next loop. + int[] extra = new int[code.Count]; + Stack stack = new Stack(); + int currentBeginExceptionBlock = -1; + int currentLast = -1; + for (int i = 0; i < code.Count; i++) + { + switch (code[i].pseudo) + { + case CodeType.BeginExceptionBlock: + stack.Push(currentBeginExceptionBlock); + currentBeginExceptionBlock = i; + currentLast = i; + break; + case CodeType.EndExceptionBlock: + extra[currentLast] = i; + extra[i] = currentBeginExceptionBlock; + currentBeginExceptionBlock = stack.Pop(); + currentLast = currentBeginExceptionBlock; + if (currentLast != -1) + { + while (extra[currentLast] != 0) + { + currentLast = extra[currentLast]; + } + } + break; + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + extra[currentLast] = i; + currentLast = i; + break; + } + } + + // Now we look for consecutive exception blocks that have the same fault handler + for (int i = 0; i < code.Count - 1; i++) + { + if (code[i].pseudo == CodeType.EndExceptionBlock + && code[i + 1].pseudo == CodeType.BeginExceptionBlock) + { + if (IsFaultOnlyBlock(extra, extra[i]) && IsFaultOnlyBlock(extra, i + 1)) + { + int beginFault1 = extra[extra[i]]; + int beginFault2 = extra[i + 1]; + int length1 = extra[beginFault1] - beginFault1; + int length2 = extra[beginFault2] - beginFault2; + if (length1 == length2 && MatchHandlers(beginFault1, beginFault2, length1)) + { + // Check if the labels at the start of the handler are reachable from outside + // of the new combined block. + for (int j = i + 2; j < beginFault2; j++) + { + if (code[j].pseudo == CodeType.OpCode) + { + break; + } + else if (code[j].pseudo == CodeType.Label) + { + if (HasBranchTo(0, extra[i], code[j].Label) + || HasBranchTo(beginFault2 + length2, code.Count, code[j].Label)) + { + goto no_merge; + } + } + } + // Merge the two blocks by overwritting the first fault block and + // the BeginExceptionBlock of the second block. + for (int j = beginFault1; j < i + 2; j++) + { + code[j] = new OpCodeWrapper(OpCodes.Nop, null); + } + // Repair the linking structure. + extra[extra[i]] = beginFault2; + extra[extra[beginFault2]] = extra[i]; + } + } + no_merge: ; + } + } + } + + private bool HasBranchTo(int start, int end, CodeEmitterLabel label) + { + for (int i = start; i < end; i++) + { + if (code[i].HasLabel) + { + if (code[i].Label == label) + { + return true; + } + } + else if (code[i].opcode == OpCodes.Switch) + { + foreach (CodeEmitterLabel swlbl in code[i].Labels) + { + if (swlbl == label) + { + return true; + } + } + } + } + return false; + } + + private bool MatchHandlers(int beginFault1, int beginFault2, int length) + { + for (int i = 0; i < length; i++) + { + if (!code[beginFault1 + i].Match(code[beginFault2 + i])) + { + return false; + } + } + return true; + } + + private bool IsFaultOnlyBlock(int[] extra, int begin) + { + return code[extra[begin]].pseudo == CodeType.BeginFaultBlock + && code[extra[extra[begin]]].pseudo == CodeType.EndExceptionBlock; } internal void DoEmit() @@ -1280,16 +1558,20 @@ namespace IKVM.Internal OptimizePatterns(); #if STATIC_COMPILER - for (int i = 0; i < 2; i++) + for (int i = 0; i < 4; i++) { RemoveJumpNext(); ChaseBranches(); RemoveUnusedLabels(); - BubblePopsAndReleaseTempLocals(); + SortPseudoOpCodes(); AnnihilatePops(); AnnihilateStoreReleaseTempLocals(); + DeduplicateBranchSourceTargetCode(); + OptimizeStackTransfer(); + MergeExceptionBlocks(); RemoveDeadCode(); } + //DumpMethod(); OptimizeEncodings(); OptimizeBranchSizes(); @@ -1315,11 +1597,11 @@ namespace IKVM.Internal { if (code[i].pseudo == CodeType.OpCode) { - Console.WriteLine(code[i].opcode.Name); + Console.WriteLine(" " + code[i].opcode.Name); } else { - Console.WriteLine(code[i].pseudo); + Console.WriteLine(code[i]); } } }