IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
CodeEmitter.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2012 Jeroen Frijters
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Collections.Generic;
26using System.Runtime.InteropServices;
27using System.Diagnostics.SymbolStore;
28using System.Diagnostics;
29
30#if IMPORTER
31using IKVM.Reflection;
33
34using Type = IKVM.Reflection.Type;
35#else
36using System.Reflection;
37using System.Reflection.Emit;
38#endif
39
40namespace IKVM.Runtime
41{
42
44 {
45
46 readonly RuntimeContext context;
47
48 MethodInfo objectToString;
49 MethodInfo verboseCastFailure;
50 MethodInfo monitorEnter;
51 MethodInfo monitorExit;
52 MethodInfo memoryBarrier;
53
59 {
60 this.context = context ?? throw new ArgumentNullException(nameof(context));
61 }
62
63 public MethodInfo ObjectToStringMethod => objectToString ??= context.Types.Object.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance, null, [], null);
64
65 public MethodInfo VerboseCastFailureMethod => verboseCastFailure ??= JVM.SafeGetEnvironmentVariable("IKVM_VERBOSE_CAST") == null ? null : context.ByteCodeHelperMethods.VerboseCastFailure;
66
67 public MethodInfo MonitorEnterMethod => monitorEnter ??= context.Resolver.ResolveCoreType(typeof(System.Threading.Monitor).FullName).AsReflection().GetMethod("Enter", BindingFlags.Public | BindingFlags.Static, null, [context.Types.Object], null);
68
69 public MethodInfo MonitorExitMethod => monitorExit ??= context.Resolver.ResolveCoreType(typeof(System.Threading.Monitor).FullName).AsReflection().GetMethod("Exit", BindingFlags.Public | BindingFlags.Static, null, [context.Types.Object], null);
70
71 public MethodInfo MemoryBarrierMethod => memoryBarrier ??= context.Resolver.ResolveCoreType(typeof(System.Threading.Thread).FullName).GetMethod("MemoryBarrier", []).AsReflection();
72
73 public bool ExperimentalOptimizations = JVM.SafeGetEnvironmentVariable("IKVM_EXPERIMENTAL_OPTIMIZATIONS") != null;
74
80 public CodeEmitter Create(MethodBuilder mb)
81 {
82 return new CodeEmitter(context, mb.GetILGenerator(), mb.DeclaringType);
83 }
84
85#if IMPORTER == false
86
92 public CodeEmitter Create(DynamicMethod dm)
93 {
94 return new CodeEmitter(context, dm.GetILGenerator(), null);
95 }
96
97#endif
98
99 }
100
101 sealed class CodeEmitter
102 {
103
104 readonly RuntimeContext context;
105
106 ILGenerator ilgen_real;
107 bool inFinally;
108 Stack<bool> exceptionStack = new Stack<bool>();
110 CodeEmitterLocal[] tempLocals = new CodeEmitterLocal[32];
111 ISymbolDocumentWriter symbols;
112 List<OpCodeWrapper> code = new List<OpCodeWrapper>(10);
113 readonly Type declaringType;
114
115 enum CodeType : short
116 {
117
118 Unreachable,
119 OpCode,
120 BeginScope,
121 EndScope,
122 DeclareLocal,
123 ReleaseTempLocal,
124 SequencePoint,
125 LineNumber,
126 Label,
127 BeginExceptionBlock,
128 BeginCatchBlock,
129 BeginFaultBlock,
130 BeginFinallyBlock,
131 EndExceptionBlock,
132 MemoryBarrier,
133 TailCallPrevention,
134 ClearStack,
135 MonitorEnter,
136 MonitorExit,
137
138 }
139
140 enum CodeTypeFlags : short
141 {
142 None = 0,
143 EndFaultOrFinally = 1,
144 }
145
146 struct OpCodeWrapper
147 {
148
149 internal readonly CodeType pseudo;
150 readonly CodeTypeFlags flags;
151 internal readonly OpCode opcode;
152 readonly object data;
153
159 internal OpCodeWrapper(CodeType pseudo, object data)
160 {
161 this.pseudo = pseudo;
162 this.flags = CodeTypeFlags.None;
163 this.opcode = OpCodes.Nop;
164 this.data = data;
165 }
166
172 internal OpCodeWrapper(CodeType pseudo, CodeTypeFlags flags)
173 {
174 this.pseudo = pseudo;
175 this.flags = flags;
176 this.opcode = OpCodes.Nop;
177 this.data = null;
178 }
179
185 internal OpCodeWrapper(OpCode opcode, object data)
186 {
187 this.pseudo = CodeType.OpCode;
188 this.flags = CodeTypeFlags.None;
189 this.opcode = opcode;
190 this.data = data;
191 }
192
193 internal bool Match(OpCodeWrapper other) => other.pseudo == pseudo && other.opcode == opcode && (other.data == data || (data != null && data.Equals(other.data)));
194
195 internal bool HasLabel => data is CodeEmitterLabel;
196
197 internal CodeEmitterLabel Label => (CodeEmitterLabel)data;
198
199 internal bool MatchLabel(OpCodeWrapper other) => data == other.data;
200
201 internal CodeEmitterLabel[] Labels => (CodeEmitterLabel[])data;
202
203 internal bool HasLocal => data is CodeEmitterLocal;
204
205 internal CodeEmitterLocal Local => (CodeEmitterLocal)data;
206
207 internal bool MatchLocal(OpCodeWrapper other) => data == other.data;
208
209 internal bool HasValueByte => data is byte;
210
211 internal byte ValueByte => (byte)data;
212
213 internal short ValueInt16 => (short)data;
214
215 internal int ValueInt32 => (int)data;
216
217 internal long ValueInt64 => (long)data;
218
219 internal Type Type => (Type)data;
220
221 internal FieldInfo FieldInfo => (FieldInfo)data;
222
223 internal MethodBase MethodBase => (MethodBase)data;
224
225 internal void RealEmit(int ilOffset, CodeEmitter codeEmitter, ref int lineNumber)
226 {
227 if (pseudo == CodeType.OpCode)
228 {
229 if (lineNumber != -1)
230 {
231 codeEmitter.linenums ??= new IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter(32);
232 codeEmitter.linenums.AddMapping(ilOffset, lineNumber);
233 lineNumber = -1;
234 }
235
236 codeEmitter.RealEmitOpCode(opcode, data);
237 }
238 else if (pseudo == CodeType.LineNumber)
239 {
240 lineNumber = (int)data;
241 }
242 else
243 {
244 codeEmitter.RealEmitPseudoOpCode(ilOffset, pseudo, data);
245 }
246 }
247
249 public override readonly string ToString() => pseudo == CodeType.OpCode ? opcode.ToString() + " " + data : pseudo.ToString() + " " + data;
250
251 }
252
253 sealed class CalliWrapper
254 {
255
256 internal readonly CallingConvention unmanagedCallConv;
257 internal readonly Type returnType;
258 internal readonly Type[] parameterTypes;
259
260 internal CalliWrapper(CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes)
261 {
262 this.unmanagedCallConv = unmanagedCallConv;
263 this.returnType = returnType;
264 this.parameterTypes = parameterTypes == null ? null : (Type[])parameterTypes.Clone();
265 }
266
267 }
268
269 sealed class ManagedCalliWrapper
270 {
271
272 internal readonly CallingConventions callConv;
273 internal readonly Type returnType;
274 internal readonly Type[] parameterTypes;
275 internal readonly Type[] optionalParameterTypes;
276
277 internal ManagedCalliWrapper(CallingConventions callConv, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
278 {
279 this.callConv = callConv;
280 this.returnType = returnType;
281 this.parameterTypes = parameterTypes == null ? null : (Type[])parameterTypes.Clone();
282 this.optionalParameterTypes = optionalParameterTypes == null ? null : (Type[])optionalParameterTypes.Clone();
283 }
284
285 }
286
293 public CodeEmitter(RuntimeContext context, ILGenerator ilgen, Type declaringType)
294 {
295 this.context = context;
296 this.ilgen_real = ilgen;
297 this.declaringType = declaringType;
298 }
299
303 public RuntimeContext Context => context;
304
305 private void EmitPseudoOpCode(CodeType type, object data)
306 {
307 code.Add(new OpCodeWrapper(type, data));
308 }
309
310 private void EmitOpCode(OpCode opcode, object arg)
311 {
312 code.Add(new OpCodeWrapper(opcode, arg));
313 }
314
315 private void RealEmitPseudoOpCode(int ilOffset, CodeType type, object data)
316 {
317 switch (type)
318 {
319 case CodeType.Unreachable:
320 break;
321 case CodeType.BeginScope:
322 ilgen_real.BeginScope();
323 break;
324 case CodeType.EndScope:
325 ilgen_real.EndScope();
326 break;
327 case CodeType.DeclareLocal:
328 ((CodeEmitterLocal)data).Declare(ilgen_real);
329 break;
330 case CodeType.ReleaseTempLocal:
331 break;
332 case CodeType.SequencePoint:
333 // MarkSequencePoint does not exist in Core, but does exist in Framework and IKVM.Reflection
334#if NETFRAMEWORK || IMPORTER
335 if (symbols != null)
336 ilgen_real.MarkSequencePoint(symbols, (int)data, 0, (int)data + 1, 0);
337#endif
338 // we emit a nop to make sure we always have an instruction associated with the sequence point
339 ilgen_real.Emit(OpCodes.Nop);
340 break;
341 case CodeType.Label:
342 ilgen_real.MarkLabel(((CodeEmitterLabel)data).Label);
343 break;
344 case CodeType.BeginExceptionBlock:
345 ilgen_real.BeginExceptionBlock();
346 break;
347 case CodeType.BeginCatchBlock:
348 ilgen_real.BeginCatchBlock((Type)data);
349 break;
350 case CodeType.BeginFaultBlock:
351 ilgen_real.BeginFaultBlock();
352 break;
353 case CodeType.BeginFinallyBlock:
354 ilgen_real.BeginFinallyBlock();
355 break;
356 case CodeType.EndExceptionBlock:
357 ilgen_real.EndExceptionBlock();
358 ilgen_real.Emit(OpCodes.Br_S, (sbyte)-2); // bogus target of implicit leave
359 break;
360 case CodeType.MemoryBarrier:
361 ilgen_real.Emit(OpCodes.Call, context.CodeEmitterFactory.MemoryBarrierMethod);
362 break;
363 case CodeType.MonitorEnter:
364 ilgen_real.Emit(OpCodes.Call, context.CodeEmitterFactory.MonitorEnterMethod);
365 break;
366 case CodeType.MonitorExit:
367 ilgen_real.Emit(OpCodes.Call, context.CodeEmitterFactory.MonitorExitMethod);
368 break;
369 case CodeType.TailCallPrevention:
370 ilgen_real.Emit(OpCodes.Ldnull);
371 ilgen_real.Emit(OpCodes.Pop);
372 break;
373 case CodeType.ClearStack:
374 ilgen_real.Emit(OpCodes.Leave_S, (byte)0);
375 break;
376 default:
377 throw new InvalidOperationException();
378 }
379 }
380
387 void RealEmitOpCode(OpCode opcode, object arg)
388 {
389 if (arg == null)
390 {
391 ilgen_real.Emit(opcode);
392 }
393 else if (arg is int ina)
394 {
395 ilgen_real.Emit(opcode, ina);
396 }
397 else if (arg is long l)
398 {
399 ilgen_real.Emit(opcode, l);
400 }
401 else if (arg is MethodInfo mi)
402 {
403 ilgen_real.Emit(opcode, mi);
404 }
405 else if (arg is ConstructorInfo ci)
406 {
407 ilgen_real.Emit(opcode, ci);
408 }
409 else if (arg is FieldInfo fi)
410 {
411 ilgen_real.Emit(opcode, fi);
412 }
413 else if (arg is sbyte sby)
414 {
415 ilgen_real.Emit(opcode, sby);
416 }
417 else if (arg is byte by)
418 {
419 ilgen_real.Emit(opcode, by);
420 }
421 else if (arg is short sh)
422 {
423 ilgen_real.Emit(opcode, sh);
424 }
425 else if (arg is float f)
426 {
427 ilgen_real.Emit(opcode, f);
428 }
429 else if (arg is double d)
430 {
431 ilgen_real.Emit(opcode, d);
432 }
433 else if (arg is string s)
434 {
435 ilgen_real.Emit(opcode, s);
436 }
437 else if (arg is Type type)
438 {
439 ilgen_real.Emit(opcode, type);
440 }
441 else if (arg is CodeEmitterLocal local)
442 {
443 local.Emit(ilgen_real, opcode);
444 }
445 else if (arg is CodeEmitterLabel label)
446 {
447 ilgen_real.Emit(opcode, label.Label);
448 }
449 else if (arg is CodeEmitterLabel[] labels)
450 {
451 var real = new Label[labels.Length];
452 for (int i = 0; i < labels.Length; i++)
453 real[i] = labels[i].Label;
454
455 ilgen_real.Emit(opcode, real);
456 }
457 else if (arg is ManagedCalliWrapper margs)
458 {
459 ilgen_real.EmitCalli(opcode, margs.callConv, margs.returnType, margs.parameterTypes, margs.optionalParameterTypes);
460 }
461 else if (arg is CalliWrapper args)
462 {
463 ilgen_real.EmitCalli(opcode, args.unmanagedCallConv, args.returnType, args.parameterTypes);
464 }
465 else
466 {
467 throw new InvalidOperationException();
468 }
469 }
470
471 void RemoveJumpNext()
472 {
473 for (int i = 1; i < code.Count; i++)
474 {
475 if (code[i].pseudo == CodeType.Label)
476 {
477 if (code[i - 1].opcode == OpCodes.Br
478 && code[i - 1].MatchLabel(code[i]))
479 {
480 code.RemoveAt(i - 1);
481 i--;
482 }
483 else if (i >= 2
484 && code[i - 1].pseudo == CodeType.LineNumber
485 && code[i - 2].opcode == OpCodes.Br
486 && code[i - 2].MatchLabel(code[i]))
487 {
488 code.RemoveAt(i - 2);
489 i--;
490 }
491 }
492 }
493 }
494
495 void AnnihilateStoreReleaseTempLocals()
496 {
497 for (int i = 1; i < code.Count; i++)
498 {
499 if (code[i].opcode == OpCodes.Stloc)
500 {
501 if (code[i + 1].pseudo == CodeType.ReleaseTempLocal && code[i].Local == code[i + 1].Local)
502 {
503 code[i] = new OpCodeWrapper(OpCodes.Pop, null);
504 }
505 else if (code[i + 1].opcode == OpCodes.Ldloc && code[i + 1].Local == code[i].Local && code[i + 2].pseudo == CodeType.ReleaseTempLocal && code[i + 2].Local == code[i].Local)
506 {
507 code.RemoveRange(i, 2);
508 }
509 }
510 }
511 }
512
513 void AnnihilatePops()
514 {
515 for (int i = 1; i < code.Count; i++)
516 {
517 if (code[i].opcode == OpCodes.Pop)
518 {
519 // search backwards for a candidate push to annihilate
520 int stack = 0;
521 for (int j = i - 1; j >= 0; j--)
522 {
523 if (IsSideEffectFreePush(j))
524 {
525 if (stack == 0)
526 {
527 code.RemoveAt(i);
528 code.RemoveAt(j);
529 i -= 2;
530 break;
531 }
532 stack++;
533 }
534 else if (code[j].opcode == OpCodes.Stloc)
535 {
536 stack--;
537 }
538 else if (code[j].opcode == OpCodes.Shl || code[j].opcode == OpCodes.And || code[j].opcode == OpCodes.Add || code[j].opcode == OpCodes.Sub)
539 {
540 if (stack == 0)
541 break;
542
543 stack--;
544 }
545 else if (code[j].opcode == OpCodes.Conv_Ovf_I4
546 || code[j].opcode == OpCodes.Conv_I8
547 || code[j].opcode == OpCodes.Ldlen)
548 {
549 if (stack == 0)
550 {
551 break;
552 }
553 // no stack effect
554 }
555 else
556 {
557 break;
558 }
559 }
560 }
561 }
562 }
563
569 bool IsSideEffectFreePush(int index)
570 {
571 if (code[index].opcode == OpCodes.Ldstr)
572 {
573 return true;
574 }
575 else if (code[index].opcode == OpCodes.Ldnull)
576 {
577 return true;
578 }
579 else if (code[index].opcode == OpCodes.Ldsfld)
580 {
581 var field = code[index].FieldInfo;
582 if (field != null)
583 {
584 // Here we are considering BeforeFieldInit to mean that we really don't care about
585 // when the type is initialized (which is what we mean in the rest of the IKVM code as well)
586 // but it is good to point it out here because strictly speaking we're violating the
587 // BeforeFieldInit contract here by considering dummy loads not to be field accesses.
588 if ((field.DeclaringType.Attributes & TypeAttributes.BeforeFieldInit) != 0)
589 {
590 return true;
591 }
592 // If we're accessing a field in the current type, it can't trigger the static initializer
593 // (unless beforefieldinit is set, but see above for that scenario)
594 if (field.DeclaringType == declaringType)
595 {
596 return true;
597 }
598 }
599 return false;
600 }
601 else if (code[index].opcode == OpCodes.Ldc_I4)
602 {
603 return true;
604 }
605 else if (code[index].opcode == OpCodes.Ldc_I8)
606 {
607 return true;
608 }
609 else if (code[index].opcode == OpCodes.Ldc_R4)
610 {
611 return true;
612 }
613 else if (code[index].opcode == OpCodes.Ldc_R8)
614 {
615 return true;
616 }
617 else if (code[index].opcode == OpCodes.Ldloc)
618 {
619 return true;
620 }
621 else if (code[index].opcode == OpCodes.Ldarg)
622 {
623 return true;
624 }
625 else if (code[index].opcode == OpCodes.Ldarg_S)
626 {
627 return true;
628 }
629 else if (code[index].opcode == OpCodes.Ldarg_0)
630 {
631 return true;
632 }
633 else if (code[index].opcode == OpCodes.Ldarg_1)
634 {
635 return true;
636 }
637 else if (code[index].opcode == OpCodes.Ldarg_2)
638 {
639 return true;
640 }
641 else if (code[index].opcode == OpCodes.Ldarg_3)
642 {
643 return true;
644 }
645 else
646 {
647 return false;
648 }
649 }
650
651 void OptimizePatterns()
652 {
653 SetLabelRefCounts();
654 for (int i = 1; i < code.Count; i++)
655 {
656 if (code[i].opcode == OpCodes.Isinst
657 && code[i + 1].opcode == OpCodes.Ldnull
658 && code[i + 2].opcode == OpCodes.Cgt_Un
659 && (code[i + 3].opcode == OpCodes.Brfalse || code[i + 3].opcode == OpCodes.Brtrue))
660 {
661 code.RemoveRange(i + 1, 2);
662 }
663 else if (code[i].opcode == OpCodes.Ldelem_I1
664 && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 255
665 && code[i + 2].opcode == OpCodes.And)
666 {
667 code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null);
668 code.RemoveRange(i + 1, 2);
669 }
670 else if (code[i].opcode == OpCodes.Ldelem_I1
671 && code[i + 1].opcode == OpCodes.Conv_I8
672 && code[i + 2].opcode == OpCodes.Ldc_I8 && code[i + 2].ValueInt64 == 255
673 && code[i + 3].opcode == OpCodes.And)
674 {
675 code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null);
676 code.RemoveRange(i + 2, 2);
677 }
678 else if (code[i].opcode == OpCodes.Ldc_I4
679 && code[i + 1].opcode == OpCodes.Ldc_I4
680 && code[i + 2].opcode == OpCodes.And)
681 {
682 code[i] = new OpCodeWrapper(OpCodes.Ldc_I4, code[i].ValueInt32 & code[i + 1].ValueInt32);
683 code.RemoveRange(i + 1, 2);
684 }
685 else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, context.Types.Double) // dcmpl
686 || MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, context.Types.Single)) // fcmpl
687 {
688 PatchCompare(i, OpCodes.Ble_Un, OpCodes.Blt_Un, OpCodes.Bge, OpCodes.Bgt);
689 }
690 else if (MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, context.Types.Double) // dcmpg
691 || MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, context.Types.Single)) // fcmpg
692 {
693 PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge_Un, OpCodes.Bgt_Un);
694 }
695 else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt, context.Types.Int64)) // lcmp
696 {
697 PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge, OpCodes.Bgt);
698 }
699 else if (i < code.Count - 10
700 && code[i].opcode == OpCodes.Ldc_I4
701 && code[i + 1].opcode == OpCodes.Dup
702 && code[i + 2].opcode == OpCodes.Ldc_I4_M1
703 && code[i + 3].opcode == OpCodes.Bne_Un
704 && code[i + 4].opcode == OpCodes.Pop
705 && code[i + 5].opcode == OpCodes.Neg
706 && code[i + 6].opcode == OpCodes.Br
707 && code[i + 7].pseudo == CodeType.Label && code[i + 7].MatchLabel(code[i + 3]) && code[i + 7].Label.Temp == 1
708 && code[i + 8].opcode == OpCodes.Div
709 && code[i + 9].pseudo == CodeType.Label && code[i + 9].Label == code[i + 6].Label && code[i + 9].Label.Temp == 1)
710 {
711 int divisor = code[i].ValueInt32;
712 if (divisor == -1)
713 {
714 code[i] = code[i + 5];
715 code.RemoveRange(i + 1, 9);
716 }
717 else
718 {
719 code[i + 1] = code[i + 8];
720 code.RemoveRange(i + 2, 8);
721 }
722 }
723 else if (i < code.Count - 11
724 && code[i].opcode == OpCodes.Ldc_I8
725 && code[i + 1].opcode == OpCodes.Dup
726 && code[i + 2].opcode == OpCodes.Ldc_I4_M1
727 && code[i + 3].opcode == OpCodes.Conv_I8
728 && code[i + 4].opcode == OpCodes.Bne_Un
729 && code[i + 5].opcode == OpCodes.Pop
730 && code[i + 6].opcode == OpCodes.Neg
731 && code[i + 7].opcode == OpCodes.Br
732 && code[i + 8].pseudo == CodeType.Label && code[i + 8].MatchLabel(code[i + 4]) && code[i + 8].Label.Temp == 1
733 && code[i + 9].opcode == OpCodes.Div
734 && code[i + 10].pseudo == CodeType.Label && code[i + 10].MatchLabel(code[i + 7]) && code[i + 10].Label.Temp == 1)
735 {
736 long divisor = code[i].ValueInt64;
737 if (divisor == -1)
738 {
739 code[i] = code[i + 6];
740 code.RemoveRange(i + 1, 10);
741 }
742 else
743 {
744 code[i + 1] = code[i + 9];
745 code.RemoveRange(i + 2, 9);
746 }
747 }
748 else if (code[i].opcode == OpCodes.Box
749 && code[i + 1].opcode == OpCodes.Unbox && code[i + 1].Type == code[i].Type)
750 {
751 CodeEmitterLocal local = new CodeEmitterLocal(code[i].Type);
752 code[i] = new OpCodeWrapper(OpCodes.Stloc, local);
753 code[i + 1] = new OpCodeWrapper(OpCodes.Ldloca, local);
754 }
755 else if (i < code.Count - 13
756 && code[i + 0].opcode == OpCodes.Box
757 && code[i + 1].opcode == OpCodes.Dup
758 && code[i + 2].opcode == OpCodes.Brtrue
759 && code[i + 3].opcode == OpCodes.Pop
760 && code[i + 4].opcode == OpCodes.Ldloca && code[i + 4].Local.LocalType == code[i + 0].Type
761 && code[i + 5].opcode == OpCodes.Initobj && code[i + 5].Type == code[i + 0].Type
762 && code[i + 6].opcode == OpCodes.Ldloc && code[i + 6].Local == code[i + 4].Local
763 && code[i + 7].pseudo == CodeType.ReleaseTempLocal && code[i + 7].Local == code[i + 6].Local
764 && code[i + 8].opcode == OpCodes.Br
765 && code[i + 9].pseudo == CodeType.Label && code[i + 9].MatchLabel(code[i + 2]) && code[i + 9].Label.Temp == 1
766 && code[i + 10].opcode == OpCodes.Unbox && code[i + 10].Type == code[i + 0].Type
767 && code[i + 11].opcode == OpCodes.Ldobj && code[i + 11].Type == code[i + 0].Type
768 && code[i + 12].pseudo == CodeType.Label && code[i + 12].MatchLabel(code[i + 8]) && code[i + 12].Label.Temp == 1)
769 {
770 code.RemoveRange(i, 13);
771 }
772
773 // NOTE intentionally not an else, because we want to optimize the code generated by the earlier compare optimization
774 if (i < code.Count - 6
775 && code[i].opcode.FlowControl == FlowControl.Cond_Branch
776 && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 1
777 && code[i + 2].opcode == OpCodes.Br
778 && code[i + 3].pseudo == CodeType.Label && code[i + 3].MatchLabel(code[i]) && code[i + 3].Label.Temp == 1
779 && code[i + 4].opcode == OpCodes.Ldc_I4 && code[i + 4].ValueInt32 == 0
780 && code[i + 5].pseudo == CodeType.Label && code[i + 5].MatchLabel(code[i + 2]) && code[i + 5].Label.Temp == 1)
781 {
782 if (code[i].opcode == OpCodes.Bne_Un)
783 {
784 code[i] = new OpCodeWrapper(OpCodes.Ceq, null);
785 code.RemoveRange(i + 1, 5);
786 }
787 else if (code[i].opcode == OpCodes.Beq)
788 {
789 code[i + 0] = new OpCodeWrapper(OpCodes.Ceq, null);
790 code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0);
791 code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null);
792 code.RemoveRange(i + 3, 3);
793 }
794 else if (code[i].opcode == OpCodes.Ble || code[i].opcode == OpCodes.Ble_Un)
795 {
796 code[i] = new OpCodeWrapper(OpCodes.Cgt, null);
797 code.RemoveRange(i + 1, 5);
798 }
799 else if (code[i].opcode == OpCodes.Blt)
800 {
801 code[i] = new OpCodeWrapper(OpCodes.Clt, null);
802 code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0);
803 code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null);
804 code.RemoveRange(i + 3, 3);
805 }
806 else if (code[i].opcode == OpCodes.Blt_Un)
807 {
808 code[i] = new OpCodeWrapper(OpCodes.Clt_Un, null);
809 code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0);
810 code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null);
811 code.RemoveRange(i + 3, 3);
812 }
813 else if (code[i].opcode == OpCodes.Bge || code[i].opcode == OpCodes.Bge_Un)
814 {
815 code[i] = new OpCodeWrapper(OpCodes.Clt, null);
816 code.RemoveRange(i + 1, 5);
817 }
818 else if (code[i].opcode == OpCodes.Bgt)
819 {
820 code[i] = new OpCodeWrapper(OpCodes.Cgt, null);
821 code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0);
822 code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null);
823 code.RemoveRange(i + 3, 3);
824 }
825 else if (code[i].opcode == OpCodes.Bgt_Un)
826 {
827 code[i] = new OpCodeWrapper(OpCodes.Cgt_Un, null);
828 code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0);
829 code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null);
830 code.RemoveRange(i + 3, 3);
831 }
832 }
833 }
834 }
835
836 bool MatchCompare(int index, OpCode cmp1, OpCode cmp2, Type type)
837 {
838 return code[index].opcode == OpCodes.Stloc && code[index].Local.LocalType == type
839 && code[index + 1].opcode == OpCodes.Stloc && code[index + 1].Local.LocalType == type
840 && code[index + 2].opcode == OpCodes.Ldloc && code[index + 2].MatchLocal(code[index + 1])
841 && code[index + 3].opcode == OpCodes.Ldloc && code[index + 3].MatchLocal(code[index])
842 && code[index + 4].opcode == cmp1
843 && code[index + 5].opcode == OpCodes.Ldloc && code[index + 5].MatchLocal(code[index + 1])
844 && code[index + 6].opcode == OpCodes.Ldloc && code[index + 6].MatchLocal(code[index])
845 && code[index + 7].opcode == cmp2
846 && code[index + 8].opcode == OpCodes.Sub
847 && code[index + 9].pseudo == CodeType.ReleaseTempLocal && code[index + 9].Local == code[index].Local
848 && code[index + 10].pseudo == CodeType.ReleaseTempLocal && code[index + 10].Local == code[index + 1].Local
849 && ((code[index + 11].opcode.FlowControl == FlowControl.Cond_Branch && code[index + 11].HasLabel) ||
850 (code[index + 11].opcode == OpCodes.Ldc_I4_0
851 && (code[index + 12].opcode.FlowControl == FlowControl.Cond_Branch && code[index + 12].HasLabel)));
852 }
853
854 void PatchCompare(int index, OpCode ble, OpCode blt, OpCode bge, OpCode bgt)
855 {
856 if (code[index + 11].opcode == OpCodes.Brtrue)
857 {
858 code[index] = new OpCodeWrapper(OpCodes.Bne_Un, code[index + 11].Label);
859 code.RemoveRange(index + 1, 11);
860 }
861 else if (code[index + 11].opcode == OpCodes.Brfalse)
862 {
863 code[index] = new OpCodeWrapper(OpCodes.Beq, code[index + 11].Label);
864 code.RemoveRange(index + 1, 11);
865 }
866 else if (code[index + 11].opcode == OpCodes.Ldc_I4_0)
867 {
868 if (code[index + 12].opcode == OpCodes.Ble)
869 {
870 code[index] = new OpCodeWrapper(ble, code[index + 12].Label);
871 code.RemoveRange(index + 1, 12);
872 }
873 else if (code[index + 12].opcode == OpCodes.Blt)
874 {
875 code[index] = new OpCodeWrapper(blt, code[index + 12].Label);
876 code.RemoveRange(index + 1, 12);
877 }
878 else if (code[index + 12].opcode == OpCodes.Bge)
879 {
880 code[index] = new OpCodeWrapper(bge, code[index + 12].Label);
881 code.RemoveRange(index + 1, 12);
882 }
883 else if (code[index + 12].opcode == OpCodes.Bgt)
884 {
885 code[index] = new OpCodeWrapper(bgt, code[index + 12].Label);
886 code.RemoveRange(index + 1, 12);
887 }
888 }
889 }
890
894 void OptimizeEncodings()
895 {
896 for (int i = 0; i < code.Count; i++)
897 {
898 if (code[i].opcode == OpCodes.Ldc_I4)
899 code[i] = OptimizeLdcI4(code[i].ValueInt32);
900 else if (code[i].opcode == OpCodes.Ldc_I8)
901 OptimizeLdcI8(i);
902 }
903 }
904
910 OpCodeWrapper OptimizeLdcI4(int value)
911 {
912 switch (value)
913 {
914 case -1:
915 return new OpCodeWrapper(OpCodes.Ldc_I4_M1, null);
916 case 0:
917 return new OpCodeWrapper(OpCodes.Ldc_I4_0, null);
918 case 1:
919 return new OpCodeWrapper(OpCodes.Ldc_I4_1, null);
920 case 2:
921 return new OpCodeWrapper(OpCodes.Ldc_I4_2, null);
922 case 3:
923 return new OpCodeWrapper(OpCodes.Ldc_I4_3, null);
924 case 4:
925 return new OpCodeWrapper(OpCodes.Ldc_I4_4, null);
926 case 5:
927 return new OpCodeWrapper(OpCodes.Ldc_I4_5, null);
928 case 6:
929 return new OpCodeWrapper(OpCodes.Ldc_I4_6, null);
930 case 7:
931 return new OpCodeWrapper(OpCodes.Ldc_I4_7, null);
932 case 8:
933 return new OpCodeWrapper(OpCodes.Ldc_I4_8, null);
934 default:
935 if (value >= -128 && value <= 127)
936 return new OpCodeWrapper(OpCodes.Ldc_I4_S, (sbyte)value);
937 else
938 return new OpCodeWrapper(OpCodes.Ldc_I4, value);
939 }
940 }
941
946 void OptimizeLdcI8(int index)
947 {
948 var value = code[index].ValueInt64;
949 if (value >= int.MinValue && value <= uint.MaxValue)
950 {
951 code[index] = OptimizeLdcI4((int)value);
952 code.Insert(index + 1, new OpCodeWrapper(value < 0 ? OpCodes.Conv_I8 : OpCodes.Conv_U8, null));
953 }
954 }
955
956 private void ChaseBranches()
957 {
958 /*
959 * Here we do a couple of different optimizations to unconditional branches:
960 * - a branch to a ret or endfinally will be replaced
961 * by the ret or endfinally instruction (because that is always at least as efficient)
962 * - a branch to a branch will remove the indirection
963 * - a leave to a branch or leave will remove the indirection
964 */
965 SetLabelIndexes();
966 for (int i = 0; i < code.Count; i++)
967 {
968 if (code[i].opcode == OpCodes.Br)
969 {
970 int target = code[i].Label.Temp + 1;
971 if (code[target].pseudo == CodeType.LineNumber)
972 {
973 // line number info on endfinally or ret is probably useless anyway
974 target++;
975 }
976 if (code[target].opcode == OpCodes.Endfinally || code[target].opcode == OpCodes.Ret)
977 {
978 code[i] = code[target];
979 }
980 else
981 {
982 CodeEmitterLabel label = null;
983 while (code[target].opcode == OpCodes.Br && target != i)
984 {
985 label = code[target].Label;
986 target = code[target].Label.Temp + 1;
987 }
988 if (label != null)
989 {
990 code[i] = new OpCodeWrapper(OpCodes.Br, label);
991 }
992 }
993 }
994 else if (code[i].opcode == OpCodes.Leave)
995 {
996 int target = code[i].Label.Temp + 1;
997 CodeEmitterLabel label = null;
998 while ((code[target].opcode == OpCodes.Br || code[target].opcode == OpCodes.Leave) && target != i)
999 {
1000 label = code[target].Label;
1001 target = code[target].Label.Temp + 1;
1002 }
1003 if (label != null)
1004 {
1005 code[i] = new OpCodeWrapper(OpCodes.Leave, label);
1006 }
1007 }
1008 }
1009 }
1010
1011 private void RemoveSingletonBranches()
1012 {
1013 /*
1014 * Here we try to remove unconditional branches that jump to a label with ref count of one
1015 * and where the code is not otherwise used.
1016 */
1017 SetLabelRefCounts();
1018 // now increment label refcounts for labels that are also reachable via the preceding instruction
1019 bool reachable = true;
1020 for (int i = 0; i < code.Count; i++)
1021 {
1022 if (reachable)
1023 {
1024 switch (code[i].pseudo)
1025 {
1026 case CodeType.Label:
1027 code[i].Label.Temp++;
1028 break;
1029 case CodeType.BeginCatchBlock:
1030 case CodeType.BeginFaultBlock:
1031 case CodeType.BeginFinallyBlock:
1032 case CodeType.EndExceptionBlock:
1033 throw new InvalidOperationException();
1034 case CodeType.OpCode:
1035 switch (code[i].opcode.FlowControl)
1036 {
1037 case FlowControl.Branch:
1038 case FlowControl.Return:
1039 case FlowControl.Throw:
1040 reachable = false;
1041 break;
1042 }
1043 break;
1044 }
1045 }
1046 else
1047 {
1048 switch (code[i].pseudo)
1049 {
1050 case CodeType.Label:
1051 reachable = code[i].Label.Temp > 0;
1052 break;
1053 case CodeType.BeginCatchBlock:
1054 case CodeType.BeginFaultBlock:
1055 case CodeType.BeginFinallyBlock:
1056 reachable = true;
1057 break;
1058 }
1059 }
1060 }
1061
1062 // now remove the unconditional branches to labels with a refcount of one
1063 for (int i = 0; i < code.Count; i++)
1064 {
1065 if (code[i].opcode == OpCodes.Br && code[i].Label.Temp == 1)
1066 {
1067 int target = FindLabel(code[i].Label) + 1;
1068 for (int j = target; j < code.Count; j++)
1069 {
1070 switch (code[j].pseudo)
1071 {
1072 case CodeType.OpCode:
1073 if (code[j].HasLocal && FindLocal(code[j].Local) > i)
1074 {
1075 // we cannot local variable usage before the declaration
1076 goto breakOuter;
1077 }
1078 switch (code[j].opcode.FlowControl)
1079 {
1080 case FlowControl.Branch:
1081 case FlowControl.Return:
1082 case FlowControl.Throw:
1083 // we've found a viable sequence of opcode to move to the branch location
1084 List<OpCodeWrapper> range = code.GetRange(target, j - target + 1);
1085 if (target < i)
1086 {
1087 code.RemoveAt(i);
1088 code.InsertRange(i, range);
1089 code.RemoveRange(target - 1, range.Count + 1);
1090 i -= range.Count + 1;
1091 }
1092 else
1093 {
1094 code.RemoveRange(target - 1, range.Count + 1);
1095 code.RemoveAt(i);
1096 code.InsertRange(i, range);
1097 }
1098 goto breakOuter;
1099 }
1100 break;
1101 case CodeType.Label:
1102 case CodeType.BeginExceptionBlock:
1103 case CodeType.DeclareLocal:
1104 goto breakOuter;
1105 }
1106 }
1107 breakOuter:;
1108 }
1109 }
1110 }
1111
1112 private int FindLabel(CodeEmitterLabel label)
1113 {
1114 for (int i = 0; i < code.Count; i++)
1115 {
1116 if (code[i].pseudo == CodeType.Label && code[i].Label == label)
1117 {
1118 return i;
1119 }
1120 }
1121 throw new InvalidOperationException();
1122 }
1123
1124 private int FindLocal(CodeEmitterLocal local)
1125 {
1126 for (int i = 0; i < code.Count; i++)
1127 {
1128 if (code[i].pseudo == CodeType.DeclareLocal && code[i].Local == local)
1129 {
1130 return i;
1131 }
1132 }
1133 // if the local variable isn't declared, it is a temporary that is allocated on demand
1134 // (so we can move their usage freely)
1135 return 0;
1136 }
1137
1138 private void SortPseudoOpCodes()
1139 {
1140 for (int i = 0; i < code.Count - 1; i++)
1141 {
1142 switch (code[i].pseudo)
1143 {
1144 case CodeType.ReleaseTempLocal:
1145 for (int j = i - 1; ; j--)
1146 {
1147 if (j == -1)
1148 {
1149 code.RemoveAt(i);
1150 break;
1151 }
1152 if (code[j].HasLocal && code[j].Local == code[i].Local)
1153 {
1154 MoveInstruction(i, j + 1);
1155 break;
1156 }
1157 }
1158 break;
1159 }
1160 }
1161 }
1162
1163 private void MoveInstruction(int i, int j)
1164 {
1165 if (i == j - 1 || i == j + 1)
1166 {
1167 OpCodeWrapper temp = code[i];
1168 code[i] = code[j];
1169 code[j] = temp;
1170 }
1171 else if (i < j)
1172 {
1173 code.Insert(j, code[i]);
1174 code.RemoveAt(i);
1175 }
1176 else if (i > j)
1177 {
1178 OpCodeWrapper temp = code[i];
1179 code.RemoveAt(i);
1180 code.Insert(j, temp);
1181 }
1182 }
1183
1184 private void ClearLabelTemp()
1185 {
1186 for (int i = 0; i < code.Count; i++)
1187 {
1188 if (code[i].pseudo == CodeType.Label)
1189 {
1190 code[i].Label.Temp = 0;
1191 }
1192 }
1193 }
1194
1195 private void SetLabelIndexes()
1196 {
1197 for (int i = 0; i < code.Count; i++)
1198 {
1199 if (code[i].pseudo == CodeType.Label)
1200 {
1201 code[i].Label.Temp = i;
1202 }
1203 }
1204 }
1205
1206 private void SetLabelRefCounts()
1207 {
1208 ClearLabelTemp();
1209 for (int i = 0; i < code.Count; i++)
1210 {
1211 if (code[i].pseudo == CodeType.OpCode)
1212 {
1213 if (code[i].HasLabel)
1214 {
1215 code[i].Label.Temp++;
1216 }
1217 else if (code[i].opcode == OpCodes.Switch)
1218 {
1219 foreach (CodeEmitterLabel label in code[i].Labels)
1220 {
1221 label.Temp++;
1222 }
1223 }
1224 }
1225 }
1226 }
1227
1228 private void RemoveUnusedLabels()
1229 {
1230 SetLabelRefCounts();
1231 for (int i = 0; i < code.Count; i++)
1232 {
1233 while (code[i].pseudo == CodeType.Label && code[i].Label.Temp == 0)
1234 {
1235 code.RemoveAt(i);
1236 }
1237 }
1238 }
1239
1240 private void RemoveDeadCode()
1241 {
1242 ClearLabelTemp();
1243 const int ReachableFlag = 1;
1244 const int ProcessedFlag = 2;
1245 bool reachable = true;
1246 bool done = false;
1247 while (!done)
1248 {
1249 done = true;
1250 for (int i = 0; i < code.Count; i++)
1251 {
1252 if (reachable)
1253 {
1254 if (code[i].pseudo == CodeType.Label)
1255 {
1256 if (code[i].Label.Temp == ProcessedFlag)
1257 {
1258 done = false;
1259 }
1260 code[i].Label.Temp |= ReachableFlag;
1261 }
1262 else if (code[i].pseudo == CodeType.OpCode)
1263 {
1264 if (code[i].HasLabel)
1265 {
1266 if (code[i].Label.Temp == ProcessedFlag)
1267 {
1268 done = false;
1269 }
1270 code[i].Label.Temp |= ReachableFlag;
1271 }
1272 else if (code[i].opcode == OpCodes.Switch)
1273 {
1274 foreach (CodeEmitterLabel label in code[i].Labels)
1275 {
1276 if (label.Temp == ProcessedFlag)
1277 {
1278 done = false;
1279 }
1280 label.Temp |= ReachableFlag;
1281 }
1282 }
1283 switch (code[i].opcode.FlowControl)
1284 {
1285 case FlowControl.Cond_Branch:
1286 if (!code[i].HasLabel && code[i].opcode != OpCodes.Switch)
1287 {
1288 throw new NotSupportedException();
1289 }
1290 break;
1291 case FlowControl.Branch:
1292 case FlowControl.Return:
1293 case FlowControl.Throw:
1294 reachable = false;
1295 break;
1296 }
1297 }
1298 }
1299 else if (code[i].pseudo == CodeType.BeginCatchBlock)
1300 {
1301 reachable = true;
1302 }
1303 else if (code[i].pseudo == CodeType.BeginFaultBlock)
1304 {
1305 reachable = true;
1306 }
1307 else if (code[i].pseudo == CodeType.BeginFinallyBlock)
1308 {
1309 reachable = true;
1310 }
1311 else if (code[i].pseudo == CodeType.Label && (code[i].Label.Temp & ReachableFlag) != 0)
1312 {
1313 reachable = true;
1314 }
1315 if (code[i].pseudo == CodeType.Label)
1316 {
1317 code[i].Label.Temp |= ProcessedFlag;
1318 }
1319 }
1320 }
1321 reachable = true;
1322 int firstUnreachable = -1;
1323 for (int i = 0; i < code.Count; i++)
1324 {
1325 if (reachable)
1326 {
1327 if (code[i].pseudo == CodeType.OpCode)
1328 {
1329 switch (code[i].opcode.FlowControl)
1330 {
1331 case FlowControl.Branch:
1332 case FlowControl.Return:
1333 case FlowControl.Throw:
1334 reachable = false;
1335 firstUnreachable = i + 1;
1336 break;
1337 }
1338 }
1339 }
1340 else
1341 {
1342 switch (code[i].pseudo)
1343 {
1344 case CodeType.OpCode:
1345 break;
1346 case CodeType.Label:
1347 if ((code[i].Label.Temp & ReachableFlag) != 0)
1348 {
1349 goto case CodeType.BeginCatchBlock;
1350 }
1351 break;
1352 case CodeType.BeginCatchBlock:
1353 case CodeType.BeginFaultBlock:
1354 case CodeType.BeginFinallyBlock:
1355 code.RemoveRange(firstUnreachable, i - firstUnreachable);
1356 i = firstUnreachable;
1357 firstUnreachable = -1;
1358 reachable = true;
1359 break;
1360 default:
1361 code.RemoveRange(firstUnreachable, i - firstUnreachable);
1362 i = firstUnreachable;
1363 firstUnreachable++;
1364 break;
1365 }
1366 }
1367 }
1368 if (!reachable)
1369 {
1370 code.RemoveRange(firstUnreachable, code.Count - firstUnreachable);
1371 }
1372
1373 // TODO can't we incorporate this in the above code?
1374 // remove exception blocks with empty try blocks
1375 // (which can happen if the try block is unreachable)
1376 for (int i = 0; i < code.Count; i++)
1377 {
1378 restart:
1379 if (code[i].pseudo == CodeType.BeginExceptionBlock)
1380 {
1381 for (int k = 0; ; k++)
1382 {
1383 switch (code[i + k].pseudo)
1384 {
1385 case CodeType.BeginCatchBlock:
1386 case CodeType.BeginFaultBlock:
1387 case CodeType.BeginFinallyBlock:
1388 int depth = 0;
1389 for (int j = i + 1; ; j++)
1390 {
1391 switch (code[j].pseudo)
1392 {
1393 case CodeType.BeginExceptionBlock:
1394 depth++;
1395 break;
1396 case CodeType.EndExceptionBlock:
1397 if (depth == 0)
1398 {
1399 code.RemoveRange(i, (j - i) + 1);
1400 goto restart;
1401 }
1402 depth--;
1403 break;
1404 }
1405 }
1406 case CodeType.OpCode:
1407 goto next;
1408 }
1409 }
1410 }
1411 next:;
1412 }
1413 }
1414
1415 private void DeduplicateBranchSourceTargetCode()
1416 {
1417 SetLabelIndexes();
1418 for (int i = 0; i < code.Count; i++)
1419 {
1420 if (code[i].opcode == OpCodes.Br && code[i].HasLabel)
1421 {
1422 int source = i - 1;
1423 int target = code[i].Label.Temp - 1;
1424 while (source >= 0 && target >= 0)
1425 {
1426 switch (code[source].pseudo)
1427 {
1428 case CodeType.LineNumber:
1429 case CodeType.OpCode:
1430 break;
1431 default:
1432 goto break_while;
1433 }
1434 if (!code[source].Match(code[target]))
1435 {
1436 break;
1437 }
1438 switch (code[source].opcode.FlowControl)
1439 {
1440 case FlowControl.Branch:
1441 case FlowControl.Cond_Branch:
1442 goto break_while;
1443 }
1444 source--;
1445 target--;
1446 }
1447 break_while:;
1448 source++;
1449 target++;
1450 if (source != i && target > 0 && source != target - 1)
1451 {
1452 // TODO for now we only do this optimization if there happens to be an appriopriate label
1453 if (code[target - 1].pseudo == CodeType.Label)
1454 {
1455 code[source] = new OpCodeWrapper(OpCodes.Br, code[target - 1].Label);
1456 for (int j = source + 1; j <= i; j++)
1457 {
1458 // We can't depend on DCE for code correctness (we have to maintain all MSIL invariants at all times),
1459 // so we patch out the unused code.
1460 code[j] = new OpCodeWrapper(CodeType.Unreachable, null);
1461 }
1462 }
1463 }
1464 }
1465 }
1466 }
1467
1468 private void OptimizeStackTransfer()
1469 {
1470 for (int i = 0; i < code.Count; i++)
1471 {
1472 if (code[i].opcode == OpCodes.Ldloc &&
1473 code[i + 1].opcode == OpCodes.Stloc &&
1474 code[i + 2].pseudo == CodeType.BeginExceptionBlock &&
1475 code[i + 3].opcode == OpCodes.Ldloc &&
1476 code[i + 3].MatchLocal(code[i + 1]) &&
1477 code[i + 4].pseudo == CodeType.ReleaseTempLocal &&
1478 code[i + 4].MatchLocal(code[i + 3]))
1479 {
1480 code[i + 1] = code[i];
1481 code[i] = code[i + 2];
1482 code.RemoveRange(i + 2, 3);
1483 }
1484 }
1485 }
1486
1487 private void MergeExceptionBlocks()
1488 {
1489 // The first loop will convert all Begin[Exception|Catch|Fault|Finally]Block and EndExceptionBlock
1490 // pseudo opcodes into a cyclic linked list (EndExceptionBlock links back to BeginExceptionBlock)
1491 // to allow for easy traversal in the next loop.
1492 int[] extra = new int[code.Count];
1493 Stack<int> stack = new Stack<int>();
1494 int currentBeginExceptionBlock = -1;
1495 int currentLast = -1;
1496 for (int i = 0; i < code.Count; i++)
1497 {
1498 switch (code[i].pseudo)
1499 {
1500 case CodeType.BeginExceptionBlock:
1501 stack.Push(currentBeginExceptionBlock);
1502 currentBeginExceptionBlock = i;
1503 currentLast = i;
1504 break;
1505 case CodeType.EndExceptionBlock:
1506 extra[currentLast] = i;
1507 extra[i] = currentBeginExceptionBlock;
1508 currentBeginExceptionBlock = stack.Pop();
1509 currentLast = currentBeginExceptionBlock;
1510 if (currentLast != -1)
1511 {
1512 while (extra[currentLast] != 0)
1513 {
1514 currentLast = extra[currentLast];
1515 }
1516 }
1517 break;
1518 case CodeType.BeginCatchBlock:
1519 case CodeType.BeginFaultBlock:
1520 case CodeType.BeginFinallyBlock:
1521 extra[currentLast] = i;
1522 currentLast = i;
1523 break;
1524 }
1525 }
1526
1527 // Now we look for consecutive exception blocks that have the same fault handler
1528 for (int i = 0; i < code.Count - 1; i++)
1529 {
1530 if (code[i].pseudo == CodeType.EndExceptionBlock
1531 && code[i + 1].pseudo == CodeType.BeginExceptionBlock)
1532 {
1533 if (IsFaultOnlyBlock(extra, extra[i]) && IsFaultOnlyBlock(extra, i + 1))
1534 {
1535 int beginFault1 = extra[extra[i]];
1536 int beginFault2 = extra[i + 1];
1537 int length1 = extra[beginFault1] - beginFault1;
1538 int length2 = extra[beginFault2] - beginFault2;
1539 if (length1 == length2 && MatchHandlers(beginFault1, beginFault2, length1))
1540 {
1541 // Check if the labels at the start of the handler are reachable from outside
1542 // of the new combined block.
1543 for (int j = i + 2; j < beginFault2; j++)
1544 {
1545 if (code[j].pseudo == CodeType.OpCode)
1546 {
1547 break;
1548 }
1549 else if (code[j].pseudo == CodeType.Label)
1550 {
1551 if (HasBranchTo(0, extra[i], code[j].Label)
1552 || HasBranchTo(beginFault2 + length2, code.Count, code[j].Label))
1553 {
1554 goto no_merge;
1555 }
1556 }
1557 }
1558 // Merge the two blocks by overwritting the first fault block and
1559 // the BeginExceptionBlock of the second block.
1560 for (int j = beginFault1; j < i + 2; j++)
1561 {
1562 code[j] = new OpCodeWrapper(OpCodes.Nop, null);
1563 }
1564 // Repair the linking structure.
1565 extra[extra[i]] = beginFault2;
1566 extra[extra[beginFault2]] = extra[i];
1567 }
1568 }
1569 no_merge:;
1570 }
1571 }
1572 }
1573
1581 bool HasBranchTo(int start, int end, CodeEmitterLabel label)
1582 {
1583 for (int i = start; i < end; i++)
1584 {
1585 if (code[i].HasLabel)
1586 {
1587 if (code[i].Label == label)
1588 return true;
1589 }
1590 else if (code[i].opcode == OpCodes.Switch)
1591 {
1592 foreach (CodeEmitterLabel swlbl in code[i].Labels)
1593 if (swlbl == label)
1594 return true;
1595 }
1596 }
1597
1598 return false;
1599 }
1600
1601 private bool MatchHandlers(int beginFault1, int beginFault2, int length)
1602 {
1603 for (int i = 0; i < length; i++)
1604 if (!code[beginFault1 + i].Match(code[beginFault2 + i]))
1605 return false;
1606
1607 return true;
1608 }
1609
1610 private bool IsFaultOnlyBlock(int[] extra, int begin)
1611 {
1612 return code[extra[begin]].pseudo == CodeType.BeginFaultBlock && code[extra[extra[begin]]].pseudo == CodeType.EndExceptionBlock;
1613 }
1614
1615 private void ConvertSynchronizedFaultToFinally()
1616 {
1617 bool labelIndexSet = false;
1618 int start = -1;
1619 int nest = 0;
1620 int next = -1;
1621 for (int i = 0; i < code.Count; i++)
1622 {
1623 switch (code[i].pseudo)
1624 {
1625 case CodeType.BeginExceptionBlock:
1626 if (nest == 0)
1627 {
1628 start = i;
1629 }
1630 else if (nest == 1 && next <= start)
1631 {
1632 next = i;
1633 }
1634 nest++;
1635 break;
1636 case CodeType.BeginCatchBlock:
1637 case CodeType.BeginFinallyBlock:
1638 if (nest == 1)
1639 {
1640 nest = 0;
1641 if (next > start)
1642 {
1643 // while we were processing the outer block, we encountered a nested BeginExceptionBlock
1644 // so now that we've failed the outer, restart at the first nested block
1645 i = start = next;
1646 nest = 1;
1647 }
1648 }
1649 else
1650 {
1651 next = -1;
1652 }
1653 break;
1654 case CodeType.BeginFaultBlock:
1655 if (nest == 1)
1656 {
1657 int beginFault = i;
1658 if (code[i + 1].pseudo == CodeType.LineNumber)
1659 {
1660 i++;
1661 }
1662 // check if the fault handler is the synchronized block exit pattern
1663 if (code[i + 1].opcode == OpCodes.Ldloc
1664 && code[i + 2].pseudo == CodeType.MonitorExit
1665 && code[i + 3].opcode == OpCodes.Endfinally)
1666 {
1667 if (!labelIndexSet)
1668 {
1669 labelIndexSet = true;
1670 SetLabelIndexes();
1671 }
1672 // now make two passes through the try block to 1) see if all leave
1673 // opcodes that leave the try block do a synchronized block exit
1674 // and 2) patch out the synchronized block exit
1675 for (int pass = 0; pass < 2; pass++)
1676 {
1677 for (int j = start; j < i; j++)
1678 {
1679 if (code[j].opcode == OpCodes.Leave)
1680 {
1681 int target = code[j].Label.Temp;
1682 if (target < start || target > i)
1683 {
1684 // check if the code preceding the leave matches the fault block
1685 if ((code[j - 1].opcode == OpCodes.Pop || code[j - 1].opcode == OpCodes.Stloc)
1686 && code[j - 2].pseudo == CodeType.MonitorExit
1687 && code[j - 3].Match(code[i + 1]))
1688 {
1689 if (pass == 1)
1690 {
1691 // move the leave to the top of the sequence we're removing
1692 code[j - 3] = code[j - 1];
1693 code[j - 2] = code[j - 0];
1694 code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None);
1695 code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None);
1696 }
1697 }
1698 else if (code[j - 1].pseudo == CodeType.MonitorExit
1699 && code[j - 2].Match(code[i + 1]))
1700 {
1701 if (pass == 1)
1702 {
1703 // move the leave to the top of the sequence we're removing
1704 code[j - 2] = code[j];
1705 code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None);
1706 code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None);
1707 }
1708 }
1709 else
1710 {
1711 goto fail;
1712 }
1713 }
1714 }
1715 }
1716 }
1717 // if we end up here, all leaves have been successfully patched,
1718 // so now we turn the BeginFaultBlock into a BeginFinallyBlock
1719 code[beginFault] = new OpCodeWrapper(CodeType.BeginFinallyBlock, CodeTypeFlags.None);
1720 fail:;
1721 }
1722 goto case CodeType.BeginFinallyBlock;
1723 }
1724 break;
1725 case CodeType.EndExceptionBlock:
1726 nest--;
1727 break;
1728 }
1729 }
1730 }
1731
1732 private void RemoveRedundantMemoryBarriers()
1733 {
1734 int lastMemoryBarrier = -1;
1735 for (int i = 0; i < code.Count; i++)
1736 {
1737 switch (code[i].pseudo)
1738 {
1739 case CodeType.MemoryBarrier:
1740 if (lastMemoryBarrier != -1)
1741 {
1742 code.RemoveAt(lastMemoryBarrier);
1743 i--;
1744 }
1745 lastMemoryBarrier = i;
1746 break;
1747 case CodeType.OpCode:
1748 if (code[i].opcode == OpCodes.Volatile)
1749 {
1750 if (code[i + 1].opcode != OpCodes.Stfld && code[i + 1].opcode != OpCodes.Stsfld)
1751 {
1752 lastMemoryBarrier = -1;
1753 }
1754 }
1755 else if (code[i].opcode.FlowControl != FlowControl.Next)
1756 {
1757 lastMemoryBarrier = -1;
1758 }
1759 break;
1760 }
1761 }
1762 }
1763
1764 private static bool MatchLdarg(OpCodeWrapper opc, out short arg)
1765 {
1766 if (opc.opcode == OpCodes.Ldarg)
1767 {
1768 arg = opc.ValueInt16;
1769 return true;
1770 }
1771 else if (opc.opcode == OpCodes.Ldarg_S)
1772 {
1773 arg = opc.ValueByte;
1774 return true;
1775 }
1776 else if (opc.opcode == OpCodes.Ldarg_0)
1777 {
1778 arg = 0;
1779 return true;
1780 }
1781 else if (opc.opcode == OpCodes.Ldarg_1)
1782 {
1783 arg = 1;
1784 return true;
1785 }
1786 else if (opc.opcode == OpCodes.Ldarg_2)
1787 {
1788 arg = 2;
1789 return true;
1790 }
1791 else if (opc.opcode == OpCodes.Ldarg_3)
1792 {
1793 arg = 3;
1794 return true;
1795 }
1796 else
1797 {
1798 arg = -1;
1799 return false;
1800 }
1801 }
1802
1803 private bool IsBranchEqNe(OpCode opcode)
1804 {
1805 return opcode == OpCodes.Beq
1806 || opcode == OpCodes.Bne_Un;
1807 }
1808
1809 private void CLRv4_x64_JIT_Workaround()
1810 {
1811 for (int i = 0; i < code.Count - 2; i++)
1812 {
1813 // This is a workaround for https://connect.microsoft.com/VisualStudio/feedback/details/566946/x64-jit-optimization-bug
1814 //
1815 // Testing shows that the bug appears to be very specific and requires a comparison of a method argument with zero.
1816 // For example, the problem goes away when the method argument is first assigned to a local variable and then
1817 // the comparison (and subsequent use) is done against the local variable.
1818 //
1819 // This means we only have to detect these specific patterns:
1820 //
1821 // ldc.i8 0x0 ldarg
1822 // ldarg ldc.i8 0x0
1823 // beq/bne beq/bne
1824 //
1825 // The workaround is to replace ldarg with ldarga/ldind.i8. Looking at the generated code by the x86 and x64 JITs
1826 // this appears to be as efficient as the ldarg and it avoids the x64 bug.
1827 if (code[i].opcode == OpCodes.Ldc_I8 && code[i].ValueInt64 == 0)
1828 {
1829 short arg;
1830 int m;
1831 if (i > 0 && MatchLdarg(code[i - 1], out arg) && IsBranchEqNe(code[i + 1].opcode))
1832 {
1833 m = i - 1;
1834 }
1835 else if (MatchLdarg(code[i + 1], out arg) && IsBranchEqNe(code[i + 2].opcode))
1836 {
1837 m = i + 1;
1838 }
1839 else
1840 {
1841 continue;
1842 }
1843 code[m] = new OpCodeWrapper(OpCodes.Ldarga, arg);
1844 code.Insert(m + 1, new OpCodeWrapper(OpCodes.Ldind_I8, null));
1845 }
1846 }
1847 }
1848
1849 void CheckInvariants()
1850 {
1851 CheckInvariantBranchInOrOutOfBlocks();
1852 CheckInvariantOpCodeUsage();
1853 CheckInvariantLocalVariables();
1854 }
1855
1856 void CheckInvariantBranchInOrOutOfBlocks()
1857 {
1858 /*
1859 * We maintain an invariant that a branch (other than an explicit leave)
1860 * can never branch out or into an exception block (try or handler).
1861 * This is a stronger invariant than requirement by MSIL, because
1862 * we also disallow the following sequence:
1863 *
1864 * Br Label0
1865 * ...
1866 * BeginExceptionBlock
1867 * Label0:
1868 * ...
1869 * Br Label0
1870 *
1871 * This should be rewritten as:
1872 *
1873 * Br Label0
1874 * ...
1875 * Label0:
1876 * BeginExceptionBlock
1877 * Label1:
1878 * ...
1879 * Br Label1
1880 */
1881 int blockId = 0;
1882 int nextBlockId = 1;
1883 Stack<int> blocks = new Stack<int>();
1884 for (int i = 0; i < code.Count; i++)
1885 {
1886 switch (code[i].pseudo)
1887 {
1888 case CodeType.Label:
1889 code[i].Label.Temp = blockId;
1890 break;
1891 case CodeType.BeginExceptionBlock:
1892 blocks.Push(blockId);
1893 goto case CodeType.BeginFinallyBlock;
1894 case CodeType.BeginFinallyBlock:
1895 case CodeType.BeginFaultBlock:
1896 case CodeType.BeginCatchBlock:
1897 blockId = nextBlockId++;
1898 break;
1899 case CodeType.EndExceptionBlock:
1900 blockId = blocks.Pop();
1901 break;
1902 }
1903 }
1904 if (blocks.Count != 0)
1905 {
1906 throw new InvalidOperationException("Unbalanced exception blocks");
1907 }
1908 blockId = 0;
1909 nextBlockId = 1;
1910 for (int i = 0; i < code.Count; i++)
1911 {
1912 switch (code[i].pseudo)
1913 {
1914 case CodeType.OpCode:
1915 if (code[i].HasLabel
1916 && code[i].opcode != OpCodes.Leave
1917 && code[i].Label.Temp != blockId)
1918 {
1919 DumpMethod();
1920 throw new InvalidOperationException("Invalid branch " + code[i].opcode.Name + " at offset " + i + " from block " + blockId + " to " + code[i].Label.Temp);
1921 }
1922 break;
1923 case CodeType.BeginExceptionBlock:
1924 blocks.Push(blockId);
1925 goto case CodeType.BeginFinallyBlock;
1926 case CodeType.BeginFinallyBlock:
1927 case CodeType.BeginFaultBlock:
1928 case CodeType.BeginCatchBlock:
1929 blockId = nextBlockId++;
1930 break;
1931 case CodeType.EndExceptionBlock:
1932 blockId = blocks.Pop();
1933 break;
1934 }
1935 }
1936 }
1937
1938 private void CheckInvariantOpCodeUsage()
1939 {
1940 for (int i = 0; i < code.Count; i++)
1941 {
1942 switch (code[i].opcode.FlowControl)
1943 {
1944 case FlowControl.Branch:
1945 case FlowControl.Cond_Branch:
1946 if (!code[i].HasLabel && code[i].opcode != OpCodes.Switch)
1947 {
1948 throw new InvalidOperationException();
1949 }
1950 break;
1951 }
1952 }
1953 }
1954
1955 private void CheckInvariantLocalVariables()
1956 {
1957 List<CodeEmitterLocal> locals = new List<CodeEmitterLocal>();
1958 for (int i = 0; i < code.Count; i++)
1959 {
1960 if (code[i].pseudo == CodeType.DeclareLocal)
1961 {
1962 if (locals.Contains(code[i].Local))
1963 {
1964 throw new InvalidOperationException("Local variable used before declaration");
1965 }
1966 }
1967 else if (code[i].HasLocal)
1968 {
1969 locals.Add(code[i].Local);
1970 }
1971 }
1972 }
1973
1974 private void MoveLocalDeclarationToBeginScope()
1975 {
1976 int pos = 0;
1977 for (int i = 0; i < code.Count; i++)
1978 {
1979 switch (code[i].pseudo)
1980 {
1981 case CodeType.BeginScope:
1982 pos = i + 1;
1983 break;
1984 case CodeType.DeclareLocal:
1985 OpCodeWrapper decl = code[i];
1986 code.RemoveAt(i);
1987 code.Insert(pos++, decl);
1988 break;
1989 }
1990 }
1991 }
1992
1993 internal void DoEmit()
1994 {
1995 OptimizePatterns();
1996 CLRv4_x64_JIT_Workaround();
1997 RemoveRedundantMemoryBarriers();
1998
1999 if (false)
2000 {
2001 CheckInvariants();
2002 MoveLocalDeclarationToBeginScope();
2003
2004 for (int i = 0; i < 4; i++)
2005 {
2006 RemoveJumpNext();
2007 CheckInvariants();
2008 ChaseBranches();
2009 CheckInvariants();
2010 RemoveSingletonBranches();
2011 CheckInvariants();
2012 RemoveUnusedLabels();
2013 CheckInvariants();
2014 SortPseudoOpCodes();
2015 CheckInvariants();
2016 AnnihilatePops();
2017 CheckInvariants();
2018 AnnihilateStoreReleaseTempLocals();
2019 CheckInvariants();
2020 DeduplicateBranchSourceTargetCode();
2021 CheckInvariants();
2022 OptimizeStackTransfer();
2023 CheckInvariants();
2024 MergeExceptionBlocks();
2025 CheckInvariants();
2026 ConvertSynchronizedFaultToFinally();
2027 CheckInvariants();
2028 RemoveDeadCode();
2029 CheckInvariants();
2030 }
2031 }
2032
2033#if IMPORTER
2034 OptimizeEncodings();
2035#endif
2036
2037 int ilOffset = 0;
2038 int lineNumber = -1;
2039 for (int i = 0; i < code.Count; i++)
2040 {
2041 code[i].RealEmit(ilOffset, this, ref lineNumber);
2042 ilOffset = ilgen_real.ILOffset;
2043 }
2044 }
2045
2046 internal void DumpMethod()
2047 {
2048 var labelIndexes = new Dictionary<CodeEmitterLabel, int>();
2049 for (int i = 0; i < code.Count; i++)
2050 {
2051 if (code[i].pseudo == CodeType.Label)
2052 {
2053 labelIndexes.Add(code[i].Label, i);
2054 }
2055 }
2056 Console.WriteLine("======================");
2057 for (int i = 0; i < code.Count; i++)
2058 {
2059 if (code[i].pseudo == CodeType.OpCode)
2060 {
2061 Console.Write(" " + code[i].opcode.Name);
2062 if (code[i].HasLabel)
2063 {
2064 Console.Write(" label" + labelIndexes[code[i].Label]);
2065 }
2066 else if (code[i].opcode == OpCodes.Ldarg_S || code[i].opcode == OpCodes.Ldarga_S)
2067 {
2068 Console.Write(" " + code[i].ValueByte);
2069 }
2070 else if (code[i].opcode == OpCodes.Ldarg || code[i].opcode == OpCodes.Ldarga)
2071 {
2072 Console.Write(" " + code[i].ValueInt16);
2073 }
2074 else if (code[i].opcode == OpCodes.Isinst || code[i].opcode == OpCodes.Castclass || code[i].opcode == OpCodes.Box || code[i].opcode == OpCodes.Unbox || code[i].opcode == OpCodes.Ldobj || code[i].opcode == OpCodes.Newarr)
2075 {
2076 Console.Write(" " + code[i].Type);
2077 }
2078 else if (code[i].opcode == OpCodes.Call || code[i].opcode == OpCodes.Callvirt)
2079 {
2080 Console.Write(" " + code[i].MethodBase);
2081 }
2082 else if (code[i].opcode == OpCodes.Ldfld || code[i].opcode == OpCodes.Ldsfld || code[i].opcode == OpCodes.Stfld || code[i].opcode == OpCodes.Stsfld)
2083 {
2084 Console.Write(" " + code[i].FieldInfo);
2085 }
2086 else if (code[i].opcode == OpCodes.Ldc_I4)
2087 {
2088 Console.Write(" " + code[i].ValueInt32);
2089 }
2090 else if (code[i].opcode == OpCodes.Ldloc || code[i].opcode == OpCodes.Stloc)
2091 {
2092 Console.Write(" " + code[i].Local.__LocalIndex);
2093 }
2094 Console.WriteLine();
2095 }
2096 else if (code[i].pseudo == CodeType.Label)
2097 {
2098 Console.WriteLine("label{0}: // temp = {1}", i, code[i].Label.Temp);
2099 }
2100 else if (code[i].pseudo == CodeType.DeclareLocal)
2101 {
2102 Console.WriteLine("local #{0} = {1}", code[i].Local.__LocalIndex, code[i].Local.LocalType);
2103 }
2104 else
2105 {
2106 Console.WriteLine(code[i]);
2107 }
2108 }
2109 }
2110
2111 internal void DefineSymbolDocument(ModuleBuilder module, string url, Guid language, Guid languageVendor, Guid documentType)
2112 {
2113#if NETFRAMEWORK || IMPORTER
2114 symbols = module.DefineDocument(url, language, languageVendor, documentType);
2115#endif
2116 }
2117
2118 internal CodeEmitterLocal UnsafeAllocTempLocal(Type type)
2119 {
2120 int free = -1;
2121 for (int i = 0; i < tempLocals.Length; i++)
2122 {
2123 CodeEmitterLocal lb = tempLocals[i];
2124 if (lb == null)
2125 {
2126 if (free == -1)
2127 {
2128 free = i;
2129 }
2130 }
2131 else if (lb.LocalType == type)
2132 {
2133 return lb;
2134 }
2135 }
2136 CodeEmitterLocal lb1 = DeclareLocal(type);
2137 if (free != -1)
2138 {
2139 tempLocals[free] = lb1;
2140 }
2141 return lb1;
2142 }
2143
2144 internal CodeEmitterLocal AllocTempLocal(Type type)
2145 {
2146 for (int i = 0; i < tempLocals.Length; i++)
2147 {
2148 var lb = tempLocals[i];
2149 if (lb != null && lb.LocalType == type)
2150 {
2151 tempLocals[i] = null;
2152 return lb;
2153 }
2154 }
2155
2156 return new CodeEmitterLocal(type);
2157 }
2158
2159 internal void ReleaseTempLocal(CodeEmitterLocal lb)
2160 {
2161 EmitPseudoOpCode(CodeType.ReleaseTempLocal, lb);
2162
2163 for (int i = 0; i < tempLocals.Length; i++)
2164 {
2165 if (tempLocals[i] == null)
2166 {
2167 tempLocals[i] = lb;
2168 break;
2169 }
2170 }
2171 }
2172
2173 internal void BeginCatchBlock(Type exceptionType)
2174 {
2175 EmitPseudoOpCode(CodeType.BeginCatchBlock, exceptionType);
2176 }
2177
2178 internal void BeginExceptionBlock()
2179 {
2180 exceptionStack.Push(inFinally);
2181 inFinally = false;
2182 EmitPseudoOpCode(CodeType.BeginExceptionBlock, null);
2183 }
2184
2185 internal void BeginFaultBlock()
2186 {
2187 inFinally = true;
2188 EmitPseudoOpCode(CodeType.BeginFaultBlock, null);
2189 }
2190
2191 internal void BeginFinallyBlock()
2192 {
2193 inFinally = true;
2194 EmitPseudoOpCode(CodeType.BeginFinallyBlock, null);
2195 }
2196
2197 internal void BeginScope()
2198 {
2199 EmitPseudoOpCode(CodeType.BeginScope, null);
2200 }
2201
2202 internal CodeEmitterLocal DeclareLocal(Type localType)
2203 {
2204 var local = new CodeEmitterLocal(localType);
2205 EmitPseudoOpCode(CodeType.DeclareLocal, local);
2206 return local;
2207 }
2208
2209 internal CodeEmitterLabel DefineLabel()
2210 {
2211 return new CodeEmitterLabel(ilgen_real.DefineLabel());
2212 }
2213
2214 internal void Emit(OpCode opcode)
2215 {
2216 EmitOpCode(opcode, null);
2217 }
2218
2219 internal void EmitUnaligned(byte alignment)
2220 {
2221 EmitOpCode(OpCodes.Unaligned, alignment);
2222 }
2223
2224 internal void Emit(OpCode opcode, MethodBase mb)
2225 {
2226 EmitOpCode(opcode, mb);
2227 }
2228
2229 internal void EmitLdc_R8(double arg)
2230 {
2231 EmitOpCode(OpCodes.Ldc_R8, arg);
2232 }
2233
2234 internal void Emit(OpCode opcode, FieldInfo field)
2235 {
2236 EmitOpCode(opcode, field);
2237 }
2238
2239 internal void EmitLdarg(int arg)
2240 {
2241 Debug.Assert(0 <= arg && arg < 65536);
2242
2243 switch (arg)
2244 {
2245 case 0:
2246 EmitOpCode(OpCodes.Ldarg_0, null);
2247 break;
2248 case 1:
2249 EmitOpCode(OpCodes.Ldarg_1, null);
2250 break;
2251 case 2:
2252 EmitOpCode(OpCodes.Ldarg_2, null);
2253 break;
2254 case 3:
2255 EmitOpCode(OpCodes.Ldarg_3, null);
2256 break;
2257 default:
2258 if ((uint)arg <= byte.MaxValue)
2259 EmitOpCode(OpCodes.Ldarg_S, (byte)arg);
2260 else
2261 EmitOpCode(OpCodes.Ldarg, (short)arg);
2262
2263 break;
2264 }
2265 }
2266
2267 internal void EmitLdarga(int arg)
2268 {
2269 Debug.Assert(0 <= arg && arg < 65536);
2270
2271 if (arg < 256)
2272 EmitOpCode(OpCodes.Ldarga_S, (byte)arg);
2273 else
2274 EmitOpCode(OpCodes.Ldarga, (short)arg);
2275 }
2276
2277 internal void EmitStarg(int arg)
2278 {
2279 Debug.Assert(0 <= arg && arg < 65536);
2280
2281 if (arg < 256)
2282 EmitOpCode(OpCodes.Starg_S, (byte)arg);
2283 else
2284 EmitOpCode(OpCodes.Starg, (short)arg);
2285 }
2286
2287 internal void EmitLdc_I8(long arg)
2288 {
2289 EmitOpCode(OpCodes.Ldc_I8, arg);
2290 }
2291
2292 internal void EmitBr(CodeEmitterLabel label)
2293 {
2294 EmitOpCode(OpCodes.Br, label);
2295 }
2296
2297 internal void EmitBeq(CodeEmitterLabel label)
2298 {
2299 EmitOpCode(OpCodes.Beq, label);
2300 }
2301
2302 internal void EmitBne_Un(CodeEmitterLabel label)
2303 {
2304 EmitOpCode(OpCodes.Bne_Un, label);
2305 }
2306
2307 internal void EmitBle_Un(CodeEmitterLabel label)
2308 {
2309 EmitOpCode(OpCodes.Ble_Un, label);
2310 }
2311
2312 internal void EmitBlt_Un(CodeEmitterLabel label)
2313 {
2314 EmitOpCode(OpCodes.Blt_Un, label);
2315 }
2316
2317 internal void EmitBge_Un(CodeEmitterLabel label)
2318 {
2319 EmitOpCode(OpCodes.Bge_Un, label);
2320 }
2321
2322 internal void EmitBle(CodeEmitterLabel label)
2323 {
2324 EmitOpCode(OpCodes.Ble, label);
2325 }
2326
2327 internal void EmitBlt(CodeEmitterLabel label)
2328 {
2329 EmitOpCode(OpCodes.Blt, label);
2330 }
2331
2332 internal void EmitBge(CodeEmitterLabel label)
2333 {
2334 EmitOpCode(OpCodes.Bge, label);
2335 }
2336
2337 internal void EmitBgt(CodeEmitterLabel label)
2338 {
2339 EmitOpCode(OpCodes.Bgt, label);
2340 }
2341
2342 internal void EmitBrtrue(CodeEmitterLabel label)
2343 {
2344 EmitOpCode(OpCodes.Brtrue, label);
2345 }
2346
2347 internal void EmitBrfalse(CodeEmitterLabel label)
2348 {
2349 EmitOpCode(OpCodes.Brfalse, label);
2350 }
2351
2352 internal void EmitLeave(CodeEmitterLabel label)
2353 {
2354 EmitOpCode(OpCodes.Leave, label);
2355 }
2356
2357 internal void EmitSwitch(CodeEmitterLabel[] labels)
2358 {
2359 EmitOpCode(OpCodes.Switch, labels);
2360 }
2361
2362 internal void Emit(OpCode opcode, CodeEmitterLocal local)
2363 {
2364 EmitOpCode(opcode, local);
2365 }
2366
2367 internal void EmitLdc_R4(float arg)
2368 {
2369 EmitOpCode(OpCodes.Ldc_R4, arg);
2370 }
2371
2372 internal void Emit(OpCode opcode, string arg)
2373 {
2374 EmitOpCode(opcode, arg);
2375 }
2376
2377 internal void Emit(OpCode opcode, Type cls)
2378 {
2379 EmitOpCode(opcode, cls);
2380 }
2381
2382 internal void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes)
2383 {
2384 EmitOpCode(opcode, new CalliWrapper(unmanagedCallConv, returnType, parameterTypes));
2385 }
2386
2387 internal void EmitCalli(OpCode opcode, CallingConventions unmanagedCallConv, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
2388 {
2389 EmitOpCode(opcode, new ManagedCalliWrapper(unmanagedCallConv, returnType, parameterTypes, optionalParameterTypes));
2390 }
2391
2392 internal void EndExceptionBlock()
2393 {
2394 EmitPseudoOpCode(CodeType.EndExceptionBlock, inFinally ? CodeTypeFlags.EndFaultOrFinally : CodeTypeFlags.None);
2395 inFinally = exceptionStack.Pop();
2396 }
2397
2398 internal void EndScope()
2399 {
2400 EmitPseudoOpCode(CodeType.EndScope, null);
2401 }
2402
2403 internal void MarkLabel(CodeEmitterLabel loc)
2404 {
2405 EmitPseudoOpCode(CodeType.Label, loc);
2406 }
2407
2408 internal void ThrowException(Type excType)
2409 {
2410 Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes));
2411 Emit(OpCodes.Throw);
2412 }
2413
2414 internal void SetLineNumber(ushort line)
2415 {
2416#if NETFRAMEWORK || IMPORTER
2417 if (symbols != null)
2418 EmitPseudoOpCode(CodeType.SequencePoint, (int)line);
2419#endif
2420
2421 EmitPseudoOpCode(CodeType.LineNumber, (int)line);
2422 }
2423
2424 internal byte[] GetLineNumberTable()
2425 {
2426 return linenums == null ? null : linenums.ToArray();
2427 }
2428
2429#if IMPORTER
2430
2431 internal void EmitLineNumberTable(MethodBuilder mb)
2432 {
2433 if (linenums != null)
2434 context.AttributeHelper.SetLineNumberTable(mb, linenums);
2435 }
2436
2437#endif
2438
2439 internal void EmitThrow(string dottedClassName)
2440 {
2441 var exception = context.ClassLoaderFactory.GetBootstrapClassLoader().LoadClassByName(dottedClassName);
2442 var mw = exception.GetMethod("<init>", "()V", false);
2443 mw.Link();
2444 mw.EmitNewobj(this);
2445 Emit(OpCodes.Throw);
2446 }
2447
2448 internal void EmitThrow(string dottedClassName, string message)
2449 {
2450 var exception = context.ClassLoaderFactory.GetBootstrapClassLoader().LoadClassByName(dottedClassName);
2451 Emit(OpCodes.Ldstr, message);
2452 var mw = exception.GetMethod("<init>", "(Ljava.lang.String;)V", false);
2453 mw.Link();
2454 mw.EmitNewobj(this);
2455 Emit(OpCodes.Throw);
2456 }
2457
2458 internal void EmitNullCheck()
2459 {
2460 // I think this is the most efficient way to generate a NullReferenceException if the reference is null
2461 Emit(OpCodes.Ldvirtftn, context.CodeEmitterFactory.ObjectToStringMethod);
2462 Emit(OpCodes.Pop);
2463 }
2464
2465 internal void EmitCastclass(Type type)
2466 {
2467 if (context.CodeEmitterFactory.VerboseCastFailureMethod != null)
2468 {
2469 var lb = DeclareLocal(context.Types.Object);
2470 Emit(OpCodes.Stloc, lb);
2471 Emit(OpCodes.Ldloc, lb);
2472 Emit(OpCodes.Isinst, type);
2473 Emit(OpCodes.Dup);
2474 var ok = DefineLabel();
2475 EmitBrtrue(ok);
2476 Emit(OpCodes.Ldloc, lb);
2477 EmitBrfalse(ok); // handle null
2478 Emit(OpCodes.Ldtoken, type);
2479 Emit(OpCodes.Ldloc, lb);
2480 Emit(OpCodes.Call, context.CodeEmitterFactory.VerboseCastFailureMethod);
2481 MarkLabel(ok);
2482 }
2483 else
2484 {
2485 Emit(OpCodes.Castclass, type);
2486 }
2487 }
2488
2489 // This is basically the same as Castclass, except that it
2490 // throws an IncompatibleClassChangeError on failure.
2491 internal void EmitAssertType(Type type)
2492 {
2493 var isnull = DefineLabel();
2494 Emit(OpCodes.Dup);
2495 EmitBrfalse(isnull);
2496 Emit(OpCodes.Isinst, type);
2497 Emit(OpCodes.Dup);
2498 var ok = DefineLabel();
2499 EmitBrtrue(ok);
2500 EmitThrow("java.lang.IncompatibleClassChangeError");
2501 MarkLabel(isnull);
2502 Emit(OpCodes.Pop);
2503 Emit(OpCodes.Ldnull);
2504 MarkLabel(ok);
2505 }
2506
2507 internal void EmitUnboxSpecial(Type type)
2508 {
2509 // NOTE if the reference is null, we treat it as a default instance of the value type.
2510 var label1 = DefineLabel();
2511 var label2 = DefineLabel();
2512
2513 Emit(OpCodes.Dup);
2514 EmitBrtrue(label1);
2515 Emit(OpCodes.Pop);
2516 var local = AllocTempLocal(type);
2517 Emit(OpCodes.Ldloca, local);
2518 Emit(OpCodes.Initobj, type);
2519 Emit(OpCodes.Ldloc, local);
2520 ReleaseTempLocal(local);
2521 EmitBr(label2);
2522 MarkLabel(label1);
2523 Emit(OpCodes.Unbox, type);
2524 Emit(OpCodes.Ldobj, type);
2525 MarkLabel(label2);
2526 }
2527
2528 internal void EmitLdc_I4(int i)
2529 {
2530 EmitOpCode(OpCodes.Ldc_I4, i);
2531 }
2532
2533 internal void Emit_idiv()
2534 {
2535 // we need to special case dividing by -1, because the CLR div instruction
2536 // throws an OverflowException when dividing Int32.MinValue by -1, and
2537 // Java just silently overflows
2538 var label1 = DefineLabel();
2539 var label2 = DefineLabel();
2540
2541 Emit(OpCodes.Dup);
2542 Emit(OpCodes.Ldc_I4_M1);
2543 EmitBne_Un(label1);
2544 Emit(OpCodes.Pop);
2545 Emit(OpCodes.Neg);
2546 EmitBr(label2);
2547 MarkLabel(label1);
2548 Emit(OpCodes.Div);
2549 MarkLabel(label2);
2550 }
2551
2552 internal void Emit_ldiv()
2553 {
2554 // we need to special case dividing by -1, because the CLR div instruction
2555 // throws an OverflowException when dividing Int32.MinValue by -1, and
2556 // Java just silently overflows
2557 var label1 = DefineLabel();
2558 var label2 = DefineLabel();
2559
2560 Emit(OpCodes.Dup);
2561 Emit(OpCodes.Ldc_I4_M1);
2562 Emit(OpCodes.Conv_I8);
2563 EmitBne_Un(label1);
2564 Emit(OpCodes.Pop);
2565 Emit(OpCodes.Neg);
2566 EmitBr(label2);
2567 MarkLabel(label1);
2568 Emit(OpCodes.Div);
2569 MarkLabel(label2);
2570 }
2571
2572 internal void Emit_instanceof(Type type)
2573 {
2574 Emit(OpCodes.Isinst, type);
2575 Emit(OpCodes.Ldnull);
2576 Emit(OpCodes.Cgt_Un);
2577 }
2578
2579 internal enum Comparison
2580 {
2581 LessOrEqual,
2582 LessThan,
2583 GreaterOrEqual,
2584 GreaterThan
2585 }
2586
2587 internal void Emit_if_le_lt_ge_gt(Comparison comp, CodeEmitterLabel label)
2588 {
2589 // don't change this Ldc_I4_0 to Ldc_I4(0) because the optimizer recognizes only this specific pattern
2590 Emit(OpCodes.Ldc_I4_0);
2591 switch (comp)
2592 {
2593 case Comparison.LessOrEqual:
2594 EmitBle(label);
2595 break;
2596 case Comparison.LessThan:
2597 EmitBlt(label);
2598 break;
2599 case Comparison.GreaterOrEqual:
2600 EmitBge(label);
2601 break;
2602 case Comparison.GreaterThan:
2603 EmitBgt(label);
2604 break;
2605 }
2606 }
2607
2608 private void EmitCmp(Type type, OpCode cmp1, OpCode cmp2)
2609 {
2610 var value1 = AllocTempLocal(type);
2611 var value2 = AllocTempLocal(type);
2612 Emit(OpCodes.Stloc, value2);
2613 Emit(OpCodes.Stloc, value1);
2614 Emit(OpCodes.Ldloc, value1);
2615 Emit(OpCodes.Ldloc, value2);
2616 Emit(cmp1);
2617 Emit(OpCodes.Ldloc, value1);
2618 Emit(OpCodes.Ldloc, value2);
2619 Emit(cmp2);
2620 Emit(OpCodes.Sub);
2621 ReleaseTempLocal(value2);
2622 ReleaseTempLocal(value1);
2623 }
2624
2625 internal void Emit_lcmp()
2626 {
2627 EmitCmp(context.Types.Int64, OpCodes.Cgt, OpCodes.Clt);
2628 }
2629
2630 internal void Emit_fcmpl()
2631 {
2632 EmitCmp(context.Types.Single, OpCodes.Cgt, OpCodes.Clt_Un);
2633 }
2634
2635 internal void Emit_fcmpg()
2636 {
2637 EmitCmp(context.Types.Single, OpCodes.Cgt_Un, OpCodes.Clt);
2638 }
2639
2640 internal void Emit_dcmpl()
2641 {
2642 EmitCmp(context.Types.Double, OpCodes.Cgt, OpCodes.Clt_Un);
2643 }
2644
2645 internal void Emit_dcmpg()
2646 {
2647 EmitCmp(context.Types.Double, OpCodes.Cgt_Un, OpCodes.Clt);
2648 }
2649
2650 internal void Emit_And_I4(int v)
2651 {
2652 EmitLdc_I4(v);
2653 Emit(OpCodes.And);
2654 }
2655
2656 internal void CheckLabels()
2657 {
2658#if LABELCHECK
2659 foreach(System.Diagnostics.StackFrame frame in labels.Values)
2660 {
2661 string name = frame.GetFileName() + ":" + frame.GetFileLineNumber();
2662 IKVM.Runtime.JVM.CriticalFailure("Label failure: " + name, null);
2663 }
2664#endif
2665 }
2666
2667 internal void EmitMemoryBarrier()
2668 {
2669 EmitPseudoOpCode(CodeType.MemoryBarrier, null);
2670 }
2671
2672 internal void EmitTailCallPrevention()
2673 {
2674 EmitPseudoOpCode(CodeType.TailCallPrevention, null);
2675 }
2676
2677 internal void EmitClearStack()
2678 {
2679 EmitPseudoOpCode(CodeType.ClearStack, null);
2680 }
2681
2682 internal void EmitMonitorEnter()
2683 {
2684 EmitPseudoOpCode(CodeType.MonitorEnter, null);
2685 }
2686
2687 internal void EmitMonitorExit()
2688 {
2689 EmitPseudoOpCode(CodeType.MonitorExit, null);
2690 }
2691
2692 }
2693
2694}
IKVM.Reflection.Type Type
IKVM.Reflection.ConstructorInfo ConstructorInfo
IKVM.Reflection.FieldInfo FieldInfo
IKVM.Reflection.MethodInfo MethodInfo
IKVM.Reflection.MethodBase MethodBase
System.Runtime.InteropServices.CallingConvention CallingConvention
Definition Signature.cs:35
CodeEmitter Create(DynamicMethod dm)
Creates a new instance.
CodeEmitterFactory(RuntimeContext context)
Initializes a new instance.
CodeEmitter Create(MethodBuilder mb)
Creates a new instance.
RuntimeContext Context
Gets the RuntimeContext that hosts this code emitter.
CodeEmitter(RuntimeContext context, ILGenerator ilgen, Type declaringType)
Initializes a new instance.
Main state of the running JVM.
Maintains services relevant to an instane of the IKVM runtime.