1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 // Enables instruction counting and displaying stats at process exit.
6 // #define STATS
7 
8 using System.Collections.Generic;
9 using System.Diagnostics;
10 using System.Reflection;
11 using System.Runtime.CompilerServices;
12 using System.Dynamic.Utils;
13 
14 namespace System.Linq.Expressions.Interpreter
15 {
16     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")]
17     [DebuggerTypeProxy(typeof(InstructionArray.DebugView))]
18     internal readonly struct InstructionArray
19     {
20         internal readonly int MaxStackDepth;
21         internal readonly int MaxContinuationDepth;
22         internal readonly Instruction[] Instructions;
23         internal readonly object[] Objects;
24         internal readonly RuntimeLabel[] Labels;
25 
26         // list of (instruction index, cookie) sorted by instruction index:
27         internal readonly List<KeyValuePair<int, object>> DebugCookies;
28 
InstructionArraySystem.Linq.Expressions.Interpreter.InstructionArray29         internal InstructionArray(int maxStackDepth, int maxContinuationDepth, Instruction[] instructions,
30             object[] objects, RuntimeLabel[] labels, List<KeyValuePair<int, object>> debugCookies)
31         {
32             MaxStackDepth = maxStackDepth;
33             MaxContinuationDepth = maxContinuationDepth;
34             Instructions = instructions;
35             DebugCookies = debugCookies;
36             Objects = objects;
37             Labels = labels;
38         }
39 
40         #region Debug View
41 
42         internal sealed class DebugView
43         {
44             private readonly InstructionArray _array;
45 
DebugView(InstructionArray array)46             public DebugView(InstructionArray array)
47             {
48                 ContractUtils.RequiresNotNull(array, nameof(array));
49                 _array = array;
50             }
51 
52             [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
53             public InstructionList.DebugView.InstructionView[]/*!*/ A0 => GetInstructionViews(includeDebugCookies: true);
54 
GetInstructionViews(bool includeDebugCookies = false)55             public InstructionList.DebugView.InstructionView[] GetInstructionViews(bool includeDebugCookies = false)
56             {
57                 return InstructionList.DebugView.GetInstructionViews(
58                     _array.Instructions,
59                     _array.Objects,
60                     (index) => _array.Labels[index].Index,
61                     includeDebugCookies ? _array.DebugCookies : null
62                 );
63             }
64         }
65         #endregion
66     }
67 
68     [DebuggerTypeProxy(typeof(InstructionList.DebugView))]
69     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
70     internal sealed class InstructionList
71     {
72         private readonly List<Instruction> _instructions = new List<Instruction>();
73         private List<object> _objects;
74 
75         private int _currentStackDepth;
76         private int _maxStackDepth;
77         private int _currentContinuationsDepth;
78         private int _maxContinuationDepth;
79         private int _runtimeLabelCount;
80         private List<BranchLabel> _labels;
81 
82         // list of (instruction index, cookie) sorted by instruction index:
83         private List<KeyValuePair<int, object>> _debugCookies = null;
84 
85         #region Debug View
86 
87         internal sealed class DebugView
88         {
89             private readonly InstructionList _list;
90 
DebugView(InstructionList list)91             public DebugView(InstructionList list)
92             {
93                 ContractUtils.RequiresNotNull(list, nameof(list));
94                 _list = list;
95             }
96 
97             [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
98             public InstructionView[]/*!*/ A0 => GetInstructionViews(includeDebugCookies: true);
99 
GetInstructionViews(bool includeDebugCookies = false)100             public InstructionView[] GetInstructionViews(bool includeDebugCookies = false)
101             {
102                 return GetInstructionViews(
103                         _list._instructions,
104                         _list._objects,
105                         (index) => _list._labels[index].TargetIndex,
106                         includeDebugCookies ? _list._debugCookies : null
107                     );
108             }
109 
GetInstructionViews(IReadOnlyList<Instruction> instructions, IReadOnlyList<object> objects, Func<int, int> labelIndexer, IReadOnlyList<KeyValuePair<int, object>> debugCookies)110             internal static InstructionView[] GetInstructionViews(IReadOnlyList<Instruction> instructions, IReadOnlyList<object> objects,
111                 Func<int, int> labelIndexer, IReadOnlyList<KeyValuePair<int, object>> debugCookies)
112             {
113                 var result = new List<InstructionView>();
114                 int index = 0;
115                 int stackDepth = 0;
116                 int continuationsDepth = 0;
117 
118                 IEnumerator<KeyValuePair<int, object>> cookieEnumerator = (debugCookies ?? Array.Empty<KeyValuePair<int, object>>()).GetEnumerator();
119                 bool hasCookie = cookieEnumerator.MoveNext();
120 
121                 for (int i = 0, n = instructions.Count; i < n; i++)
122                 {
123                     Instruction instruction = instructions[i];
124 
125                     object cookie = null;
126                     while (hasCookie && cookieEnumerator.Current.Key == i)
127                     {
128                         cookie = cookieEnumerator.Current.Value;
129                         hasCookie = cookieEnumerator.MoveNext();
130                     }
131 
132                     int stackDiff = instruction.StackBalance;
133                     int contDiff = instruction.ContinuationsBalance;
134                     string name = instruction.ToDebugString(i, cookie, labelIndexer, objects);
135                     result.Add(new InstructionView(instruction, name, i, stackDepth, continuationsDepth));
136 
137                     index++;
138                     stackDepth += stackDiff;
139                     continuationsDepth += contDiff;
140                 }
141                 return result.ToArray();
142             }
143 
144             [DebuggerDisplay("{GetValue(),nq}", Name = "{GetName(),nq}", Type = "{GetDisplayType(), nq}")]
145             internal readonly struct InstructionView
146             {
147                 private readonly int _index;
148                 private readonly int _stackDepth;
149                 private readonly int _continuationsDepth;
150                 private readonly string _name;
151                 private readonly Instruction _instruction;
152 
GetNameSystem.Linq.Expressions.Interpreter.InstructionList.DebugView.InstructionView153                 internal string GetName()
154                 {
155                     return _index +
156                         (_continuationsDepth == 0 ? "" : " C(" + _continuationsDepth + ")") +
157                         (_stackDepth == 0 ? "" : " S(" + _stackDepth + ")");
158                 }
159 
GetValueSystem.Linq.Expressions.Interpreter.InstructionList.DebugView.InstructionView160                 internal string GetValue()
161                 {
162                     return _name;
163                 }
164 
GetDisplayTypeSystem.Linq.Expressions.Interpreter.InstructionList.DebugView.InstructionView165                 internal string GetDisplayType()
166                 {
167                     return _instruction.ContinuationsBalance + "/" + _instruction.StackBalance;
168                 }
169 
InstructionViewSystem.Linq.Expressions.Interpreter.InstructionList.DebugView.InstructionView170                 public InstructionView(Instruction instruction, string name, int index, int stackDepth, int continuationsDepth)
171                 {
172                     _instruction = instruction;
173                     _name = name;
174                     _index = index;
175                     _stackDepth = stackDepth;
176                     _continuationsDepth = continuationsDepth;
177                 }
178             }
179         }
180 
181         #endregion
182 
183         #region Core Emit Ops
184 
Emit(Instruction instruction)185         public void Emit(Instruction instruction)
186         {
187             _instructions.Add(instruction);
188             UpdateStackDepth(instruction);
189         }
190 
UpdateStackDepth(Instruction instruction)191         private void UpdateStackDepth(Instruction instruction)
192         {
193             Debug.Assert(instruction.ConsumedStack >= 0 && instruction.ProducedStack >= 0 &&
194                 instruction.ConsumedContinuations >= 0 && instruction.ProducedContinuations >= 0, "bad instruction " + instruction.ToString());
195 
196             _currentStackDepth -= instruction.ConsumedStack;
197             Debug.Assert(_currentStackDepth >= 0, "negative stack depth " + instruction.ToString());
198             _currentStackDepth += instruction.ProducedStack;
199             if (_currentStackDepth > _maxStackDepth)
200             {
201                 _maxStackDepth = _currentStackDepth;
202             }
203 
204             _currentContinuationsDepth -= instruction.ConsumedContinuations;
205             Debug.Assert(_currentContinuationsDepth >= 0, "negative continuations " + instruction.ToString());
206             _currentContinuationsDepth += instruction.ProducedContinuations;
207             if (_currentContinuationsDepth > _maxContinuationDepth)
208             {
209                 _maxContinuationDepth = _currentContinuationsDepth;
210             }
211         }
212 
213         // "Un-emit" the previous instruction.
214         // Useful if the instruction was emitted in the calling method, and covers the more usual case.
215         // In particular, calling this after an EmitPush() or EmitDup() costs about the same as adding
216         // an EmitPop() to undo it at compile time, and leaves a slightly leaner instruction list.
UnEmit()217         public void UnEmit()
218         {
219             Instruction instruction = _instructions[_instructions.Count - 1];
220             _instructions.RemoveAt(_instructions.Count - 1);
221 
222             _currentContinuationsDepth -= instruction.ProducedContinuations;
223             _currentContinuationsDepth += instruction.ConsumedContinuations;
224             _currentStackDepth -= instruction.ProducedStack;
225             _currentStackDepth += instruction.ConsumedStack;
226         }
227 
228         /// <summary>
229         /// Attaches a cookie to the last emitted instruction.
230         /// </summary>
231         [Conditional("DEBUG")]
SetDebugCookie(object cookie)232         public void SetDebugCookie(object cookie)
233         {
234 #if DEBUG
235             if (_debugCookies == null)
236             {
237                 _debugCookies = new List<KeyValuePair<int, object>>();
238             }
239 
240             Debug.Assert(Count > 0);
241             _debugCookies.Add(new KeyValuePair<int, object>(Count - 1, cookie));
242 #endif
243         }
244 
245         public int Count => _instructions.Count;
246         public int CurrentStackDepth => _currentStackDepth;
247         public int CurrentContinuationsDepth => _currentContinuationsDepth;
248         public int MaxStackDepth => _maxStackDepth;
249 
GetInstruction(int index)250         internal Instruction GetInstruction(int index) => _instructions[index];
251 
252 #if STATS
253         private static Dictionary<string, int> _executedInstructions = new Dictionary<string, int>();
254         private static Dictionary<string, Dictionary<object, bool>> _instances = new Dictionary<string, Dictionary<object, bool>>();
255 
InstructionList()256         static InstructionList()
257         {
258             AppDomain.CurrentDomain.ProcessExit += new EventHandler((_, __) =>
259             {
260                 PerfTrack.DumpHistogram(_executedInstructions);
261                 Console.WriteLine("-- Total executed: {0}", _executedInstructions.Values.Aggregate(0, (sum, value) => sum + value));
262                 Console.WriteLine("-----");
263 
264                 var referenced = new Dictionary<string, int>();
265                 int total = 0;
266                 foreach (var entry in _instances)
267                 {
268                     referenced[entry.Key] = entry.Value.Count;
269                     total += entry.Value.Count;
270                 }
271 
272                 PerfTrack.DumpHistogram(referenced);
273                 Console.WriteLine("-- Total referenced: {0}", total);
274                 Console.WriteLine("-----");
275             });
276         }
277 #endif
ToArray()278         public InstructionArray ToArray()
279         {
280 #if STATS
281             lock (_executedInstructions)
282             {
283                 _instructions.ForEach((instr) =>
284                 {
285                     int value = 0;
286                     var name = instr.GetType().Name;
287                     _executedInstructions.TryGetValue(name, out value);
288                     _executedInstructions[name] = value + 1;
289 
290                     Dictionary<object, bool> dict;
291                     if (!_instances.TryGetValue(name, out dict))
292                     {
293                         _instances[name] = dict = new Dictionary<object, bool>();
294                     }
295                     dict[instr] = true;
296                 });
297             }
298 #endif
299             return new InstructionArray(
300                 _maxStackDepth,
301                 _maxContinuationDepth,
302                 _instructions.ToArray(),
303                 _objects?.ToArray(),
304                 BuildRuntimeLabels(),
305                 _debugCookies
306             );
307         }
308 
309         #endregion
310 
311         #region Stack Operations
312 
313         private const int PushIntMinCachedValue = -100;
314         private const int PushIntMaxCachedValue = 100;
315         private const int CachedObjectCount = 256;
316 
317         private static Instruction s_null;
318         private static Instruction s_true;
319         private static Instruction s_false;
320         private static Instruction[] s_Ints;
321         private static Instruction[] s_loadObjectCached;
322 
EmitLoad(object value)323         public void EmitLoad(object value)
324         {
325             EmitLoad(value, type: null);
326         }
327 
EmitLoad(bool value)328         public void EmitLoad(bool value)
329         {
330             if (value)
331             {
332                 Emit(s_true ?? (s_true = new LoadObjectInstruction(Utils.BoxedTrue)));
333             }
334             else
335             {
336                 Emit(s_false ?? (s_false = new LoadObjectInstruction(Utils.BoxedFalse)));
337             }
338         }
339 
EmitLoad(object value, Type type)340         public void EmitLoad(object value, Type type)
341         {
342             if (value == null)
343             {
344                 Emit(s_null ?? (s_null = new LoadObjectInstruction(null)));
345                 return;
346             }
347 
348             if (type == null || type.IsValueType)
349             {
350                 if (value is bool)
351                 {
352                     EmitLoad((bool)value);
353                     return;
354                 }
355 
356                 if (value is int)
357                 {
358                     int i = (int)value;
359                     if (i >= PushIntMinCachedValue && i <= PushIntMaxCachedValue)
360                     {
361                         if (s_Ints == null)
362                         {
363                             s_Ints = new Instruction[PushIntMaxCachedValue - PushIntMinCachedValue + 1];
364                         }
365                         i -= PushIntMinCachedValue;
366                         Emit(s_Ints[i] ?? (s_Ints[i] = new LoadObjectInstruction(value)));
367                         return;
368                     }
369                 }
370             }
371 
372             if (_objects == null)
373             {
374                 _objects = new List<object>();
375                 if (s_loadObjectCached == null)
376                 {
377                     s_loadObjectCached = new Instruction[CachedObjectCount];
378                 }
379             }
380 
381             if (_objects.Count < s_loadObjectCached.Length)
382             {
383                 uint index = (uint)_objects.Count;
384                 _objects.Add(value);
385                 Emit(s_loadObjectCached[index] ?? (s_loadObjectCached[index] = new LoadCachedObjectInstruction(index)));
386             }
387             else
388             {
389                 Emit(new LoadObjectInstruction(value));
390             }
391         }
392 
EmitDup()393         public void EmitDup()
394         {
395             Emit(DupInstruction.Instance);
396         }
397 
EmitPop()398         public void EmitPop()
399         {
400             Emit(PopInstruction.Instance);
401         }
402 
403         #endregion
404 
405         #region Locals
406 
SwitchToBoxed(int index, int instructionIndex)407         internal void SwitchToBoxed(int index, int instructionIndex)
408         {
409             var instruction = _instructions[instructionIndex] as IBoxableInstruction;
410 
411             if (instruction != null)
412             {
413                 Instruction newInstruction = instruction.BoxIfIndexMatches(index);
414                 if (newInstruction != null)
415                 {
416                     _instructions[instructionIndex] = newInstruction;
417                 }
418             }
419         }
420 
421         private const int LocalInstrCacheSize = 64;
422 
423         private static Instruction[] s_loadLocal;
424         private static Instruction[] s_loadLocalBoxed;
425         private static Instruction[] s_loadLocalFromClosure;
426         private static Instruction[] s_loadLocalFromClosureBoxed;
427         private static Instruction[] s_assignLocal;
428         private static Instruction[] s_storeLocal;
429         private static Instruction[] s_assignLocalBoxed;
430         private static Instruction[] s_storeLocalBoxed;
431         private static Instruction[] s_assignLocalToClosure;
432 
EmitLoadLocal(int index)433         public void EmitLoadLocal(int index)
434         {
435             if (s_loadLocal == null)
436             {
437                 s_loadLocal = new Instruction[LocalInstrCacheSize];
438             }
439 
440             if (index < s_loadLocal.Length)
441             {
442                 Emit(s_loadLocal[index] ?? (s_loadLocal[index] = new LoadLocalInstruction(index)));
443             }
444             else
445             {
446                 Emit(new LoadLocalInstruction(index));
447             }
448         }
449 
EmitLoadLocalBoxed(int index)450         public void EmitLoadLocalBoxed(int index)
451         {
452             Emit(LoadLocalBoxed(index));
453         }
454 
LoadLocalBoxed(int index)455         internal static Instruction LoadLocalBoxed(int index)
456         {
457             if (s_loadLocalBoxed == null)
458             {
459                 s_loadLocalBoxed = new Instruction[LocalInstrCacheSize];
460             }
461 
462             if (index < s_loadLocalBoxed.Length)
463             {
464                 return s_loadLocalBoxed[index] ?? (s_loadLocalBoxed[index] = new LoadLocalBoxedInstruction(index));
465             }
466             else
467             {
468                 return new LoadLocalBoxedInstruction(index);
469             }
470         }
471 
EmitLoadLocalFromClosure(int index)472         public void EmitLoadLocalFromClosure(int index)
473         {
474             if (s_loadLocalFromClosure == null)
475             {
476                 s_loadLocalFromClosure = new Instruction[LocalInstrCacheSize];
477             }
478 
479             if (index < s_loadLocalFromClosure.Length)
480             {
481                 Emit(s_loadLocalFromClosure[index] ?? (s_loadLocalFromClosure[index] = new LoadLocalFromClosureInstruction(index)));
482             }
483             else
484             {
485                 Emit(new LoadLocalFromClosureInstruction(index));
486             }
487         }
488 
EmitLoadLocalFromClosureBoxed(int index)489         public void EmitLoadLocalFromClosureBoxed(int index)
490         {
491             if (s_loadLocalFromClosureBoxed == null)
492             {
493                 s_loadLocalFromClosureBoxed = new Instruction[LocalInstrCacheSize];
494             }
495 
496             if (index < s_loadLocalFromClosureBoxed.Length)
497             {
498                 Emit(s_loadLocalFromClosureBoxed[index] ?? (s_loadLocalFromClosureBoxed[index] = new LoadLocalFromClosureBoxedInstruction(index)));
499             }
500             else
501             {
502                 Emit(new LoadLocalFromClosureBoxedInstruction(index));
503             }
504         }
505 
EmitAssignLocal(int index)506         public void EmitAssignLocal(int index)
507         {
508             if (s_assignLocal == null)
509             {
510                 s_assignLocal = new Instruction[LocalInstrCacheSize];
511             }
512 
513             if (index < s_assignLocal.Length)
514             {
515                 Emit(s_assignLocal[index] ?? (s_assignLocal[index] = new AssignLocalInstruction(index)));
516             }
517             else
518             {
519                 Emit(new AssignLocalInstruction(index));
520             }
521         }
522 
EmitStoreLocal(int index)523         public void EmitStoreLocal(int index)
524         {
525             if (s_storeLocal == null)
526             {
527                 s_storeLocal = new Instruction[LocalInstrCacheSize];
528             }
529 
530             if (index < s_storeLocal.Length)
531             {
532                 Emit(s_storeLocal[index] ?? (s_storeLocal[index] = new StoreLocalInstruction(index)));
533             }
534             else
535             {
536                 Emit(new StoreLocalInstruction(index));
537             }
538         }
539 
EmitAssignLocalBoxed(int index)540         public void EmitAssignLocalBoxed(int index)
541         {
542             Emit(AssignLocalBoxed(index));
543         }
544 
AssignLocalBoxed(int index)545         internal static Instruction AssignLocalBoxed(int index)
546         {
547             if (s_assignLocalBoxed == null)
548             {
549                 s_assignLocalBoxed = new Instruction[LocalInstrCacheSize];
550             }
551 
552             if (index < s_assignLocalBoxed.Length)
553             {
554                 return s_assignLocalBoxed[index] ?? (s_assignLocalBoxed[index] = new AssignLocalBoxedInstruction(index));
555             }
556             else
557             {
558                 return new AssignLocalBoxedInstruction(index);
559             }
560         }
561 
EmitStoreLocalBoxed(int index)562         public void EmitStoreLocalBoxed(int index)
563         {
564             Emit(StoreLocalBoxed(index));
565         }
566 
StoreLocalBoxed(int index)567         internal static Instruction StoreLocalBoxed(int index)
568         {
569             if (s_storeLocalBoxed == null)
570             {
571                 s_storeLocalBoxed = new Instruction[LocalInstrCacheSize];
572             }
573 
574             if (index < s_storeLocalBoxed.Length)
575             {
576                 return s_storeLocalBoxed[index] ?? (s_storeLocalBoxed[index] = new StoreLocalBoxedInstruction(index));
577             }
578             else
579             {
580                 return new StoreLocalBoxedInstruction(index);
581             }
582         }
583 
EmitAssignLocalToClosure(int index)584         public void EmitAssignLocalToClosure(int index)
585         {
586             if (s_assignLocalToClosure == null)
587             {
588                 s_assignLocalToClosure = new Instruction[LocalInstrCacheSize];
589             }
590 
591             if (index < s_assignLocalToClosure.Length)
592             {
593                 Emit(s_assignLocalToClosure[index] ?? (s_assignLocalToClosure[index] = new AssignLocalToClosureInstruction(index)));
594             }
595             else
596             {
597                 Emit(new AssignLocalToClosureInstruction(index));
598             }
599         }
600 
EmitStoreLocalToClosure(int index)601         public void EmitStoreLocalToClosure(int index)
602         {
603             EmitAssignLocalToClosure(index);
604             EmitPop();
605         }
606 
EmitInitializeLocal(int index, Type type)607         public void EmitInitializeLocal(int index, Type type)
608         {
609             object value = ScriptingRuntimeHelpers.GetPrimitiveDefaultValue(type);
610             if (value != null)
611             {
612                 Emit(new InitializeLocalInstruction.ImmutableValue(index, value));
613             }
614             else if (type.IsValueType)
615             {
616                 Emit(new InitializeLocalInstruction.MutableValue(index, type));
617             }
618             else
619             {
620                 Emit(InitReference(index));
621             }
622         }
623 
EmitInitializeParameter(int index)624         internal void EmitInitializeParameter(int index)
625         {
626             Emit(Parameter(index));
627         }
628 
Parameter(int index)629         internal static Instruction Parameter(int index)
630         {
631             return new InitializeLocalInstruction.Parameter(index);
632         }
633 
ParameterBox(int index)634         internal static Instruction ParameterBox(int index)
635         {
636             return new InitializeLocalInstruction.ParameterBox(index);
637         }
638 
InitReference(int index)639         internal static Instruction InitReference(int index)
640         {
641             return new InitializeLocalInstruction.Reference(index);
642         }
643 
InitImmutableRefBox(int index)644         internal static Instruction InitImmutableRefBox(int index)
645         {
646             return new InitializeLocalInstruction.ImmutableRefBox(index);
647         }
648 
EmitNewRuntimeVariables(int count)649         public void EmitNewRuntimeVariables(int count)
650         {
651             Emit(new RuntimeVariablesInstruction(count));
652         }
653 
654         #endregion
655 
656         #region Array Operations
657 
EmitGetArrayItem()658         public void EmitGetArrayItem()
659         {
660             Emit(GetArrayItemInstruction.Instance);
661         }
662 
EmitSetArrayItem()663         public void EmitSetArrayItem()
664         {
665             Emit(SetArrayItemInstruction.Instance);
666         }
667 
EmitNewArray(Type elementType)668         public void EmitNewArray(Type elementType)
669         {
670             Emit(new NewArrayInstruction(elementType));
671         }
672 
EmitNewArrayBounds(Type elementType, int rank)673         public void EmitNewArrayBounds(Type elementType, int rank)
674         {
675             Emit(new NewArrayBoundsInstruction(elementType, rank));
676         }
677 
EmitNewArrayInit(Type elementType, int elementCount)678         public void EmitNewArrayInit(Type elementType, int elementCount)
679         {
680             Emit(new NewArrayInitInstruction(elementType, elementCount));
681         }
682 
683         #endregion
684 
685         #region Arithmetic Operations
686 
EmitAdd(Type type, bool @checked)687         public void EmitAdd(Type type, bool @checked)
688         {
689             Emit(@checked ? AddOvfInstruction.Create(type) : AddInstruction.Create(type));
690         }
691 
EmitSub(Type type, bool @checked)692         public void EmitSub(Type type, bool @checked)
693         {
694             Emit(@checked ? SubOvfInstruction.Create(type) : SubInstruction.Create(type));
695         }
696 
EmitMul(Type type, bool @checked)697         public void EmitMul(Type type, bool @checked)
698         {
699             Emit(@checked ? MulOvfInstruction.Create(type) : MulInstruction.Create(type));
700         }
701 
EmitDiv(Type type)702         public void EmitDiv(Type type)
703         {
704             Emit(DivInstruction.Create(type));
705         }
706 
EmitModulo(Type type)707         public void EmitModulo(Type type)
708         {
709             Emit(ModuloInstruction.Create(type));
710         }
711 
712         #endregion
713 
714         #region Comparisons
715 
EmitExclusiveOr(Type type)716         public void EmitExclusiveOr(Type type)
717         {
718             Emit(ExclusiveOrInstruction.Create(type));
719         }
720 
EmitAnd(Type type)721         public void EmitAnd(Type type)
722         {
723             Emit(AndInstruction.Create(type));
724         }
725 
EmitOr(Type type)726         public void EmitOr(Type type)
727         {
728             Emit(OrInstruction.Create(type));
729         }
730 
EmitLeftShift(Type type)731         public void EmitLeftShift(Type type)
732         {
733             Emit(LeftShiftInstruction.Create(type));
734         }
735 
EmitRightShift(Type type)736         public void EmitRightShift(Type type)
737         {
738             Emit(RightShiftInstruction.Create(type));
739         }
740 
EmitEqual(Type type, bool liftedToNull = false)741         public void EmitEqual(Type type, bool liftedToNull = false)
742         {
743             Emit(EqualInstruction.Create(type, liftedToNull));
744         }
745 
EmitNotEqual(Type type, bool liftedToNull = false)746         public void EmitNotEqual(Type type, bool liftedToNull = false)
747         {
748             Emit(NotEqualInstruction.Create(type, liftedToNull));
749         }
750 
EmitLessThan(Type type, bool liftedToNull)751         public void EmitLessThan(Type type, bool liftedToNull)
752         {
753             Emit(LessThanInstruction.Create(type, liftedToNull));
754         }
755 
EmitLessThanOrEqual(Type type, bool liftedToNull)756         public void EmitLessThanOrEqual(Type type, bool liftedToNull)
757         {
758             Emit(LessThanOrEqualInstruction.Create(type, liftedToNull));
759         }
760 
EmitGreaterThan(Type type, bool liftedToNull)761         public void EmitGreaterThan(Type type, bool liftedToNull)
762         {
763             Emit(GreaterThanInstruction.Create(type, liftedToNull));
764         }
765 
EmitGreaterThanOrEqual(Type type, bool liftedToNull)766         public void EmitGreaterThanOrEqual(Type type, bool liftedToNull)
767         {
768             Emit(GreaterThanOrEqualInstruction.Create(type, liftedToNull));
769         }
770 
771         #endregion
772 
773         #region Conversions
774 
EmitNumericConvertChecked(TypeCode from, TypeCode to, bool isLiftedToNull)775         public void EmitNumericConvertChecked(TypeCode from, TypeCode to, bool isLiftedToNull)
776         {
777             Emit(new NumericConvertInstruction.Checked(from, to, isLiftedToNull));
778         }
779 
EmitNumericConvertUnchecked(TypeCode from, TypeCode to, bool isLiftedToNull)780         public void EmitNumericConvertUnchecked(TypeCode from, TypeCode to, bool isLiftedToNull)
781         {
782             Emit(new NumericConvertInstruction.Unchecked(from, to, isLiftedToNull));
783         }
784 
EmitConvertToUnderlying(TypeCode to, bool isLiftedToNull)785         public void EmitConvertToUnderlying(TypeCode to, bool isLiftedToNull)
786         {
787             Emit(new NumericConvertInstruction.ToUnderlying(to, isLiftedToNull));
788         }
789 
EmitCast(Type toType)790         public void EmitCast(Type toType)
791         {
792             Emit(CastInstruction.Create(toType));
793         }
794 
EmitCastToEnum(Type toType)795         public void EmitCastToEnum(Type toType)
796         {
797             Emit(new CastToEnumInstruction(toType));
798         }
799 
EmitCastReferenceToEnum(Type toType)800         public void EmitCastReferenceToEnum(Type toType)
801         {
802             Debug.Assert(_instructions[_instructions.Count - 1] == NullCheckInstruction.Instance);
803             Emit(new CastReferenceToEnumInstruction(toType));
804         }
805 
806         #endregion
807 
808         #region Boolean Operators
809 
EmitNot(Type type)810         public void EmitNot(Type type)
811         {
812             Emit(NotInstruction.Create(type));
813         }
814 
815         #endregion
816 
817         #region Types
818 
EmitDefaultValue(Type type)819         public void EmitDefaultValue(Type type)
820         {
821             Emit(new DefaultValueInstruction(type));
822         }
823 
EmitNew(ConstructorInfo constructorInfo, ParameterInfo[] parameters)824         public void EmitNew(ConstructorInfo constructorInfo, ParameterInfo[] parameters)
825         {
826             Emit(new NewInstruction(constructorInfo, parameters.Length));
827         }
828 
EmitByRefNew(ConstructorInfo constructorInfo, ParameterInfo[] parameters, ByRefUpdater[] updaters)829         public void EmitByRefNew(ConstructorInfo constructorInfo, ParameterInfo[] parameters, ByRefUpdater[] updaters)
830         {
831             Emit(new ByRefNewInstruction(constructorInfo, parameters.Length, updaters));
832         }
833 
EmitCreateDelegate(LightDelegateCreator creator)834         internal void EmitCreateDelegate(LightDelegateCreator creator)
835         {
836             Emit(new CreateDelegateInstruction(creator));
837         }
838 
EmitTypeEquals()839         public void EmitTypeEquals()
840         {
841             Emit(TypeEqualsInstruction.Instance);
842         }
843 
EmitArrayLength()844         public void EmitArrayLength()
845         {
846             Emit(ArrayLengthInstruction.Instance);
847         }
848 
EmitNegate(Type type)849         public void EmitNegate(Type type)
850         {
851             Emit(NegateInstruction.Create(type));
852         }
853 
EmitNegateChecked(Type type)854         public void EmitNegateChecked(Type type)
855         {
856             Emit(NegateCheckedInstruction.Create(type));
857         }
858 
EmitIncrement(Type type)859         public void EmitIncrement(Type type)
860         {
861             Emit(IncrementInstruction.Create(type));
862         }
863 
EmitDecrement(Type type)864         public void EmitDecrement(Type type)
865         {
866             Emit(DecrementInstruction.Create(type));
867         }
868 
EmitTypeIs(Type type)869         public void EmitTypeIs(Type type)
870         {
871             Emit(new TypeIsInstruction(type));
872         }
873 
EmitTypeAs(Type type)874         public void EmitTypeAs(Type type)
875         {
876             Emit(new TypeAsInstruction(type));
877         }
878 
879         #endregion
880 
881         #region Fields and Methods
882 
883         private static readonly Dictionary<FieldInfo, Instruction> s_loadFields = new Dictionary<FieldInfo, Instruction>();
884 
EmitLoadField(FieldInfo field)885         public void EmitLoadField(FieldInfo field)
886         {
887             Emit(GetLoadField(field));
888         }
889 
GetLoadField(FieldInfo field)890         private Instruction GetLoadField(FieldInfo field)
891         {
892             lock (s_loadFields)
893             {
894                 Instruction instruction;
895                 if (!s_loadFields.TryGetValue(field, out instruction))
896                 {
897                     if (field.IsStatic)
898                     {
899                         instruction = new LoadStaticFieldInstruction(field);
900                     }
901                     else
902                     {
903                         instruction = new LoadFieldInstruction(field);
904                     }
905                     s_loadFields.Add(field, instruction);
906                 }
907                 return instruction;
908             }
909         }
910 
EmitStoreField(FieldInfo field)911         public void EmitStoreField(FieldInfo field)
912         {
913             if (field.IsStatic)
914             {
915                 Emit(new StoreStaticFieldInstruction(field));
916             }
917             else
918             {
919                 Emit(new StoreFieldInstruction(field));
920             }
921         }
922 
EmitCall(MethodInfo method)923         public void EmitCall(MethodInfo method)
924         {
925             EmitCall(method, method.GetParametersCached());
926         }
927 
EmitCall(MethodInfo method, ParameterInfo[] parameters)928         public void EmitCall(MethodInfo method, ParameterInfo[] parameters)
929         {
930             Emit(CallInstruction.Create(method, parameters));
931         }
932 
EmitByRefCall(MethodInfo method, ParameterInfo[] parameters, ByRefUpdater[] byrefArgs)933         public void EmitByRefCall(MethodInfo method, ParameterInfo[] parameters, ByRefUpdater[] byrefArgs)
934         {
935             Emit(new ByRefMethodInfoCallInstruction(method, method.IsStatic ? parameters.Length : parameters.Length + 1, byrefArgs));
936         }
937 
EmitNullableCall(MethodInfo method, ParameterInfo[] parameters)938         public void EmitNullableCall(MethodInfo method, ParameterInfo[] parameters)
939         {
940             Emit(NullableMethodCallInstruction.Create(method.Name, parameters.Length, method));
941         }
942 
943         #endregion
944 
945         #region Control Flow
946 
947         private static readonly RuntimeLabel[] s_emptyRuntimeLabels = new RuntimeLabel[] { new RuntimeLabel(Interpreter.RethrowOnReturn, 0, 0) };
948 
BuildRuntimeLabels()949         private RuntimeLabel[] BuildRuntimeLabels()
950         {
951             if (_runtimeLabelCount == 0)
952             {
953                 return s_emptyRuntimeLabels;
954             }
955 
956             var result = new RuntimeLabel[_runtimeLabelCount + 1];
957             foreach (BranchLabel label in _labels)
958             {
959                 if (label.HasRuntimeLabel)
960                 {
961                     result[label.LabelIndex] = label.ToRuntimeLabel();
962                 }
963             }
964             // "return and rethrow" label:
965             result[result.Length - 1] = new RuntimeLabel(Interpreter.RethrowOnReturn, 0, 0);
966             return result;
967         }
968 
MakeLabel()969         public BranchLabel MakeLabel()
970         {
971             if (_labels == null)
972             {
973                 _labels = new List<BranchLabel>();
974             }
975 
976             var label = new BranchLabel();
977             _labels.Add(label);
978             return label;
979         }
980 
FixupBranch(int branchIndex, int offset)981         internal void FixupBranch(int branchIndex, int offset)
982         {
983             _instructions[branchIndex] = ((OffsetInstruction)_instructions[branchIndex]).Fixup(offset);
984         }
985 
EnsureLabelIndex(BranchLabel label)986         private int EnsureLabelIndex(BranchLabel label)
987         {
988             if (label.HasRuntimeLabel)
989             {
990                 return label.LabelIndex;
991             }
992 
993             label.LabelIndex = _runtimeLabelCount;
994             _runtimeLabelCount++;
995             return label.LabelIndex;
996         }
997 
MarkRuntimeLabel()998         public int MarkRuntimeLabel()
999         {
1000             BranchLabel handlerLabel = MakeLabel();
1001             MarkLabel(handlerLabel);
1002             return EnsureLabelIndex(handlerLabel);
1003         }
1004 
MarkLabel(BranchLabel label)1005         public void MarkLabel(BranchLabel label)
1006         {
1007             label.Mark(this);
1008         }
1009 
EmitGoto(BranchLabel label, bool hasResult, bool hasValue, bool labelTargetGetsValue)1010         public void EmitGoto(BranchLabel label, bool hasResult, bool hasValue, bool labelTargetGetsValue)
1011         {
1012             Emit(GotoInstruction.Create(EnsureLabelIndex(label), hasResult, hasValue, labelTargetGetsValue));
1013         }
1014 
EmitBranch(OffsetInstruction instruction, BranchLabel label)1015         private void EmitBranch(OffsetInstruction instruction, BranchLabel label)
1016         {
1017             Emit(instruction);
1018             label.AddBranch(this, Count - 1);
1019         }
1020 
EmitBranch(BranchLabel label)1021         public void EmitBranch(BranchLabel label)
1022         {
1023             EmitBranch(new BranchInstruction(), label);
1024         }
1025 
EmitBranch(BranchLabel label, bool hasResult, bool hasValue)1026         public void EmitBranch(BranchLabel label, bool hasResult, bool hasValue)
1027         {
1028             EmitBranch(new BranchInstruction(hasResult, hasValue), label);
1029         }
1030 
EmitCoalescingBranch(BranchLabel leftNotNull)1031         public void EmitCoalescingBranch(BranchLabel leftNotNull)
1032         {
1033             EmitBranch(new CoalescingBranchInstruction(), leftNotNull);
1034         }
1035 
EmitBranchTrue(BranchLabel elseLabel)1036         public void EmitBranchTrue(BranchLabel elseLabel)
1037         {
1038             EmitBranch(new BranchTrueInstruction(), elseLabel);
1039         }
1040 
EmitBranchFalse(BranchLabel elseLabel)1041         public void EmitBranchFalse(BranchLabel elseLabel)
1042         {
1043             EmitBranch(new BranchFalseInstruction(), elseLabel);
1044         }
1045 
EmitThrow()1046         public void EmitThrow()
1047         {
1048             Emit(ThrowInstruction.Throw);
1049         }
1050 
EmitThrowVoid()1051         public void EmitThrowVoid()
1052         {
1053             Emit(ThrowInstruction.VoidThrow);
1054         }
1055 
EmitRethrow()1056         public void EmitRethrow()
1057         {
1058             Emit(ThrowInstruction.Rethrow);
1059         }
1060 
EmitRethrowVoid()1061         public void EmitRethrowVoid()
1062         {
1063             Emit(ThrowInstruction.VoidRethrow);
1064         }
1065 
EmitEnterTryFinally(BranchLabel finallyStartLabel)1066         public void EmitEnterTryFinally(BranchLabel finallyStartLabel)
1067         {
1068             Emit(EnterTryCatchFinallyInstruction.CreateTryFinally(EnsureLabelIndex(finallyStartLabel)));
1069         }
1070 
EmitEnterTryCatch()1071         public void EmitEnterTryCatch()
1072         {
1073             Emit(EnterTryCatchFinallyInstruction.CreateTryCatch());
1074         }
1075 
EmitEnterTryFault(BranchLabel tryEnd)1076         public EnterTryFaultInstruction EmitEnterTryFault(BranchLabel tryEnd)
1077         {
1078             var instruction = new EnterTryFaultInstruction(EnsureLabelIndex(tryEnd));
1079             Emit(instruction);
1080             return instruction;
1081         }
1082 
EmitEnterFinally(BranchLabel finallyStartLabel)1083         public void EmitEnterFinally(BranchLabel finallyStartLabel)
1084         {
1085             Emit(EnterFinallyInstruction.Create(EnsureLabelIndex(finallyStartLabel)));
1086         }
1087 
EmitLeaveFinally()1088         public void EmitLeaveFinally()
1089         {
1090             Emit(LeaveFinallyInstruction.Instance);
1091         }
1092 
EmitEnterFault(BranchLabel faultStartLabel)1093         public void EmitEnterFault(BranchLabel faultStartLabel)
1094         {
1095             Emit(EnterFaultInstruction.Create(EnsureLabelIndex(faultStartLabel)));
1096         }
1097 
EmitLeaveFault()1098         public void EmitLeaveFault()
1099         {
1100             Emit(LeaveFaultInstruction.Instance);
1101         }
1102 
EmitEnterExceptionFilter()1103         public void EmitEnterExceptionFilter()
1104         {
1105             Emit(EnterExceptionFilterInstruction.Instance);
1106         }
1107 
EmitLeaveExceptionFilter()1108         public void EmitLeaveExceptionFilter()
1109         {
1110             Emit(LeaveExceptionFilterInstruction.Instance);
1111         }
1112 
EmitEnterExceptionHandlerNonVoid()1113         public void EmitEnterExceptionHandlerNonVoid()
1114         {
1115             Emit(EnterExceptionHandlerInstruction.NonVoid);
1116         }
1117 
EmitEnterExceptionHandlerVoid()1118         public void EmitEnterExceptionHandlerVoid()
1119         {
1120             Emit(EnterExceptionHandlerInstruction.Void);
1121         }
1122 
EmitLeaveExceptionHandler(bool hasValue, BranchLabel tryExpressionEndLabel)1123         public void EmitLeaveExceptionHandler(bool hasValue, BranchLabel tryExpressionEndLabel)
1124         {
1125             Emit(LeaveExceptionHandlerInstruction.Create(EnsureLabelIndex(tryExpressionEndLabel), hasValue));
1126         }
1127 
EmitIntSwitch(Dictionary<T, int> cases)1128         public void EmitIntSwitch<T>(Dictionary<T, int> cases)
1129         {
1130             Emit(new IntSwitchInstruction<T>(cases));
1131         }
1132 
EmitStringSwitch(Dictionary<string, int> cases, StrongBox<int> nullCase)1133         public void EmitStringSwitch(Dictionary<string, int> cases, StrongBox<int> nullCase)
1134         {
1135             Emit(new StringSwitchInstruction(cases, nullCase));
1136         }
1137 
1138         #endregion
1139     }
1140 }
1141