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