Merge pull request #4155 from BrzVlad/fix-tls-lmf-addr
[mono-project.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Interpreter / Instructions / ControlFlowInstructions.cs
blobb7549ee4b77393092a14cc3f2fb3c0fe93b9fee7
1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
16 #if FEATURE_TASKS
17 using System.Threading.Tasks;
18 #endif
20 #if FEATURE_CORE_DLR
21 using System.Linq.Expressions;
22 #endif
24 using System;
25 using System.Collections.Generic;
26 using System.Diagnostics;
27 using System.Runtime.CompilerServices;
28 using System.Threading;
29 using Microsoft.Scripting.Ast;
30 using Microsoft.Scripting.Utils;
32 namespace Microsoft.Scripting.Interpreter {
33 using LoopFunc = Func<object[], StrongBox<object>[], InterpretedFrame, int>;
35 internal abstract class OffsetInstruction : Instruction {
36 internal const int Unknown = Int32.MinValue;
37 internal const int CacheSize = 32;
39 // the offset to jump to (relative to this instruction):
40 protected int _offset = Unknown;
42 public int Offset { get { return _offset; } }
43 public abstract Instruction[] Cache { get; }
45 public Instruction Fixup(int offset) {
46 Debug.Assert(_offset == Unknown && offset != Unknown);
47 _offset = offset;
49 var cache = Cache;
50 if (cache != null && offset >= 0 && offset < cache.Length) {
51 return cache[offset] ?? (cache[offset] = this);
54 return this;
57 public override string ToDebugString(int instructionIndex, object cookie, Func<int, int> labelIndexer, IList<object> objects) {
58 return ToString() + (_offset != Unknown ? " -> " + (instructionIndex + _offset).ToString() : "");
61 public override string ToString() {
62 return InstructionName + (_offset == Unknown ? "(?)" : "(" + _offset + ")");
66 internal sealed class BranchFalseInstruction : OffsetInstruction {
67 private static Instruction[] _cache;
69 public override Instruction[] Cache {
70 get {
71 if (_cache == null) {
72 _cache = new Instruction[CacheSize];
74 return _cache;
78 internal BranchFalseInstruction() {
81 public override int ConsumedStack { get { return 1; } }
83 public override int Run(InterpretedFrame frame) {
84 Debug.Assert(_offset != Unknown);
86 if (!(bool)frame.Pop()) {
87 return _offset;
90 return +1;
94 internal sealed class BranchTrueInstruction : OffsetInstruction {
95 private static Instruction[] _cache;
97 public override Instruction[] Cache {
98 get {
99 if (_cache == null) {
100 _cache = new Instruction[CacheSize];
102 return _cache;
106 internal BranchTrueInstruction() {
109 public override int ConsumedStack { get { return 1; } }
111 public override int Run(InterpretedFrame frame) {
112 Debug.Assert(_offset != Unknown);
114 if ((bool)frame.Pop()) {
115 return _offset;
118 return +1;
122 internal sealed class BranchNullInstruction : OffsetInstruction {
123 private static Instruction[] _cache;
125 public override Instruction[] Cache {
126 get {
127 if (_cache == null) {
128 _cache = new Instruction[CacheSize];
130 return _cache;
134 internal BranchNullInstruction() {
137 public override int ConsumedStack { get { return 1; } }
139 public override int Run(InterpretedFrame frame) {
140 Debug.Assert(_offset != Unknown);
142 if (frame.Pop() == null) {
143 return _offset;
146 return +1;
150 internal sealed class CoalescingBranchInstruction : OffsetInstruction {
151 private static Instruction[] _cache;
153 public override Instruction[] Cache {
154 get {
155 if (_cache == null) {
156 _cache = new Instruction[CacheSize];
158 return _cache;
162 internal CoalescingBranchInstruction() {
165 public override int ConsumedStack { get { return 1; } }
166 public override int ProducedStack { get { return 1; } }
168 public override int Run(InterpretedFrame frame) {
169 Debug.Assert(_offset != Unknown);
171 if (frame.Peek() != null) {
172 return _offset;
175 return +1;
179 internal class BranchInstruction : OffsetInstruction {
180 private static Instruction[][][] _caches;
182 public override Instruction[] Cache {
183 get {
184 if (_caches == null) {
185 _caches = new Instruction[2][][] { new Instruction[2][], new Instruction[2][] };
187 return _caches[ConsumedStack][ProducedStack] ?? (_caches[ConsumedStack][ProducedStack] = new Instruction[CacheSize]);
191 internal readonly bool _hasResult;
192 internal readonly bool _hasValue;
194 internal BranchInstruction()
195 : this(false, false) {
198 public BranchInstruction(bool hasResult, bool hasValue) {
199 _hasResult = hasResult;
200 _hasValue = hasValue;
203 public override int ConsumedStack {
204 get { return _hasValue ? 1 : 0; }
207 public override int ProducedStack {
208 get { return _hasResult ? 1 : 0; }
211 public override int Run(InterpretedFrame frame) {
212 Debug.Assert(_offset != Unknown);
214 return _offset;
218 internal abstract class IndexedBranchInstruction : Instruction {
219 protected const int CacheSize = 32;
221 internal readonly int _labelIndex;
223 public IndexedBranchInstruction(int labelIndex) {
224 _labelIndex = labelIndex;
227 public RuntimeLabel GetLabel(InterpretedFrame frame) {
228 return frame.Interpreter._labels[_labelIndex];
231 public override string ToDebugString(int instructionIndex, object cookie, Func<int, int> labelIndexer, IList<object> objects) {
232 int targetIndex = labelIndexer(_labelIndex);
233 return ToString() + (targetIndex != BranchLabel.UnknownIndex ? " -> " + targetIndex.ToString() : "");
236 public override string ToString() {
237 return InstructionName + "[" + _labelIndex + "]";
241 /// <summary>
242 /// This instruction implements a goto expression that can jump out of any expression.
243 /// It pops values (arguments) from the evaluation stack that the expression tree nodes in between
244 /// the goto expression and the target label node pushed and not consumed yet.
245 /// A goto expression can jump into a node that evaluates arguments only if it carries
246 /// a value and jumps right after the first argument (the carried value will be used as the first argument).
247 /// Goto can jump into an arbitrary child of a BlockExpression since the block doesn’t accumulate values
248 /// on evaluation stack as its child expressions are being evaluated.
249 ///
250 /// Goto needs to execute any finally blocks on the way to the target label.
251 /// <example>
252 /// {
253 /// f(1, 2, try { g(3, 4, try { goto L } finally { ... }, 6) } finally { ... }, 7, 8)
254 /// L: ...
255 /// }
256 /// </example>
257 /// The goto expression here jumps to label L while having 4 items on evaluation stack (1, 2, 3 and 4).
258 /// The jump needs to execute both finally blocks, the first one on stack level 4 the
259 /// second one on stack level 2. So, it needs to jump the first finally block, pop 2 items from the stack,
260 /// run second finally block and pop another 2 items from the stack and set instruction pointer to label L.
261 ///
262 /// Goto also needs to rethrow ThreadAbortException iff it jumps out of a catch handler and
263 /// the current thread is in "abort requested" state.
264 /// </summary>
265 internal sealed class GotoInstruction : IndexedBranchInstruction {
266 private const int Variants = 4;
267 private static readonly GotoInstruction[] Cache = new GotoInstruction[Variants * CacheSize];
269 private readonly bool _hasResult;
271 // TODO: We can remember hasValue in label and look it up when calculating stack balance. That would save some cache.
272 private readonly bool _hasValue;
274 // The values should technically be Consumed = 1, Produced = 1 for gotos that target a label whose continuation depth
275 // is different from the current continuation depth. However, in case of forward gotos, we don't not know that is the
276 // case until the label is emitted. By then the consumed and produced stack information is useless.
277 // The important thing here is that the stack balance is 0.
278 public override int ConsumedContinuations { get { return 0; } }
279 public override int ProducedContinuations { get { return 0; } }
281 public override int ConsumedStack {
282 get { return _hasValue ? 1 : 0; }
285 public override int ProducedStack {
286 get { return _hasResult ? 1 : 0; }
289 private GotoInstruction(int targetIndex, bool hasResult, bool hasValue)
290 : base(targetIndex) {
291 _hasResult = hasResult;
292 _hasValue = hasValue;
295 internal static GotoInstruction Create(int labelIndex, bool hasResult, bool hasValue) {
296 if (labelIndex < CacheSize) {
297 var index = Variants * labelIndex | (hasResult ? 2 : 0) | (hasValue ? 1 : 0);
298 return Cache[index] ?? (Cache[index] = new GotoInstruction(labelIndex, hasResult, hasValue));
300 return new GotoInstruction(labelIndex, hasResult, hasValue);
303 public override int Run(InterpretedFrame frame) {
304 // Are we jumping out of catch/finally while aborting the current thread?
305 Interpreter.AbortThreadIfRequested(frame, _labelIndex);
307 // goto the target label or the current finally continuation:
308 return frame.Goto(_labelIndex, _hasValue ? frame.Pop() : Interpreter.NoValue);
312 internal sealed class EnterTryFinallyInstruction : IndexedBranchInstruction {
313 private readonly static EnterTryFinallyInstruction[] Cache = new EnterTryFinallyInstruction[CacheSize];
315 public override int ProducedContinuations { get { return 1; } }
317 private EnterTryFinallyInstruction(int targetIndex)
318 : base(targetIndex) {
321 internal static EnterTryFinallyInstruction Create(int labelIndex) {
322 if (labelIndex < CacheSize) {
323 return Cache[labelIndex] ?? (Cache[labelIndex] = new EnterTryFinallyInstruction(labelIndex));
325 return new EnterTryFinallyInstruction(labelIndex);
328 public override int Run(InterpretedFrame frame) {
329 // Push finally.
330 frame.PushContinuation(_labelIndex);
331 return 1;
335 /// <summary>
336 /// The first instruction of finally block.
337 /// </summary>
338 internal sealed class EnterFinallyInstruction : Instruction {
339 internal static readonly Instruction Instance = new EnterFinallyInstruction();
341 public override int ProducedStack { get { return 2; } }
342 public override int ConsumedContinuations { get { return 1; } }
344 private EnterFinallyInstruction() {
347 public override int Run(InterpretedFrame frame) {
348 frame.PushPendingContinuation();
349 frame.RemoveContinuation();
350 return 1;
354 /// <summary>
355 /// The last instruction of finally block.
356 /// </summary>
357 internal sealed class LeaveFinallyInstruction : Instruction {
358 internal static readonly Instruction Instance = new LeaveFinallyInstruction();
360 public override int ConsumedStack { get { return 2; } }
362 private LeaveFinallyInstruction() {
365 public override int Run(InterpretedFrame frame) {
366 frame.PopPendingContinuation();
368 // jump to goto target or to the next finally:
369 return frame.YieldToPendingContinuation();
373 // no-op: we need this just to balance the stack depth.
374 internal sealed class EnterExceptionHandlerInstruction : Instruction {
375 internal static readonly EnterExceptionHandlerInstruction Void = new EnterExceptionHandlerInstruction(false);
376 internal static readonly EnterExceptionHandlerInstruction NonVoid = new EnterExceptionHandlerInstruction(true);
378 // True if try-expression is non-void.
379 private readonly bool _hasValue;
381 private EnterExceptionHandlerInstruction(bool hasValue) {
382 _hasValue = hasValue;
385 // If an exception is throws in try-body the expression result of try-body is not evaluated and loaded to the stack.
386 // So the stack doesn't contain the try-body's value when we start executing the handler.
387 // However, while emitting instructions try block falls thru the catch block with a value on stack.
388 // We need to declare it consumed so that the stack state upon entry to the handler corresponds to the real
389 // stack depth after throw jumped to this catch block.
390 public override int ConsumedStack { get { return _hasValue ? 1 : 0; } }
392 // A variable storing the current exception is pushed to the stack by exception handling.
393 // Catch handlers: The value is immediately popped and stored into a local.
394 // Fault handlers: The value is kept on stack during fault handler evaluation.
395 public override int ProducedStack { get { return 1; } }
397 public override int Run(InterpretedFrame frame) {
398 // nop (the exception value is pushed by the interpreter in HandleCatch)
399 return 1;
403 /// <summary>
404 /// The last instruction of a catch exception handler.
405 /// </summary>
406 internal sealed class LeaveExceptionHandlerInstruction : IndexedBranchInstruction {
407 private static LeaveExceptionHandlerInstruction[] Cache = new LeaveExceptionHandlerInstruction[2 * CacheSize];
409 private readonly bool _hasValue;
411 // The catch block yields a value if the body is non-void. This value is left on the stack.
412 public override int ConsumedStack {
413 get { return _hasValue ? 1 : 0; }
416 public override int ProducedStack {
417 get { return _hasValue ? 1 : 0; }
420 private LeaveExceptionHandlerInstruction(int labelIndex, bool hasValue)
421 : base(labelIndex) {
422 _hasValue = hasValue;
425 internal static LeaveExceptionHandlerInstruction Create(int labelIndex, bool hasValue) {
426 if (labelIndex < CacheSize) {
427 int index = (2 * labelIndex) | (hasValue ? 1 : 0);
428 return Cache[index] ?? (Cache[index] = new LeaveExceptionHandlerInstruction(labelIndex, hasValue));
430 return new LeaveExceptionHandlerInstruction(labelIndex, hasValue);
433 public override int Run(InterpretedFrame frame) {
434 // CLR rethrows ThreadAbortException when leaving catch handler if abort is requested on the current thread.
435 Interpreter.AbortThreadIfRequested(frame, _labelIndex);
436 return GetLabel(frame).Index - frame.InstructionIndex;
440 /// <summary>
441 /// The last instruction of a fault exception handler.
442 /// </summary>
443 internal sealed class LeaveFaultInstruction : Instruction {
444 internal static readonly Instruction NonVoid = new LeaveFaultInstruction(true);
445 internal static readonly Instruction Void = new LeaveFaultInstruction(false);
447 private readonly bool _hasValue;
449 // The fault block has a value if the body is non-void, but the value is never used.
450 // We compile the body of a fault block as void.
451 // However, we keep the exception object that was pushed upon entering the fault block on the stack during execution of the block
452 // and pop it at the end.
453 public override int ConsumedStack {
454 get { return 1; }
457 // While emitting instructions a non-void try-fault expression is expected to produce a value.
458 public override int ProducedStack {
459 get { return _hasValue ? 1 : 0; }
462 private LeaveFaultInstruction(bool hasValue) {
463 _hasValue = hasValue;
466 public override int Run(InterpretedFrame frame) {
467 // TODO: ThreadAbortException ?
469 object exception = frame.Pop();
470 ExceptionHandler handler;
471 return frame.Interpreter.GotoHandler(frame, exception, out handler);
476 internal sealed class ThrowInstruction : Instruction {
477 internal static readonly ThrowInstruction Throw = new ThrowInstruction(true, false);
478 internal static readonly ThrowInstruction VoidThrow = new ThrowInstruction(false, false);
479 internal static readonly ThrowInstruction Rethrow = new ThrowInstruction(true, true);
480 internal static readonly ThrowInstruction VoidRethrow = new ThrowInstruction(false, true);
482 private readonly bool _hasResult, _rethrow;
484 private ThrowInstruction(bool hasResult, bool isRethrow) {
485 _hasResult = hasResult;
486 _rethrow = isRethrow;
489 public override int ProducedStack {
490 get { return _hasResult ? 1 : 0; }
493 public override int ConsumedStack {
494 get {
495 return 1;
499 public override int Run(InterpretedFrame frame) {
500 var ex = (Exception)frame.Pop();
501 if (_rethrow) {
502 ExceptionHandler handler;
503 return frame.Interpreter.GotoHandler(frame, ex, out handler);
505 throw ex;
509 internal sealed class SwitchInstruction : Instruction {
510 private readonly Dictionary<int, int> _cases;
512 internal SwitchInstruction(Dictionary<int, int> cases) {
513 Assert.NotNull(cases);
514 _cases = cases;
517 public override int ConsumedStack { get { return 1; } }
518 public override int ProducedStack { get { return 0; } }
520 public override int Run(InterpretedFrame frame) {
521 int target;
522 return _cases.TryGetValue((int)frame.Pop(), out target) ? target : 1;
526 internal sealed class EnterLoopInstruction : Instruction {
527 private readonly int _instructionIndex;
528 private Dictionary<ParameterExpression, LocalVariable> _variables;
529 private Dictionary<ParameterExpression, LocalVariable> _closureVariables;
530 private LoopExpression _loop;
531 private int _loopEnd;
532 private int _compilationThreshold;
534 internal EnterLoopInstruction(LoopExpression loop, LocalVariables locals, int compilationThreshold, int instructionIndex) {
535 _loop = loop;
536 _variables = locals.CopyLocals();
537 _closureVariables = locals.ClosureVariables;
538 _compilationThreshold = compilationThreshold;
539 _instructionIndex = instructionIndex;
542 internal void FinishLoop(int loopEnd) {
543 _loopEnd = loopEnd;
546 public override int Run(InterpretedFrame frame) {
547 // Don't lock here, it's a frequently hit path.
549 // There could be multiple threads racing, but that is okay.
550 // Two bad things can happen:
551 // * We miss decrements (some thread sets the counter forward)
552 // * We might enter the "if" branch more than once.
554 // The first is okay, it just means we take longer to compile.
555 // The second we explicitly guard against inside of Compile().
557 // We can't miss 0. The first thread that writes -1 must have read 0 and hence start compilation.
558 if (unchecked(_compilationThreshold--) == 0) {
559 #if SILVERLIGHT
560 if (PlatformAdaptationLayer.IsCompactFramework) {
561 _compilationThreshold = Int32.MaxValue;
562 return 1;
564 #endif
565 if (frame.Interpreter.CompileSynchronously) {
566 Compile(frame);
567 } else {
568 // Kick off the compile on another thread so this one can keep going,
569 // Compile method backpatches the instruction when finished so we don't need to await the task.
570 #if FEATURE_TASKS
571 new Task(Compile, frame).Start();
572 #else
573 ThreadPool.QueueUserWorkItem(Compile, frame);
574 #endif
577 return 1;
580 private bool Compiled {
581 get { return _loop == null; }
584 private void Compile(object frameObj) {
585 if (Compiled) {
586 return;
589 lock (this) {
590 if (Compiled) {
591 return;
594 PerfTrack.NoteEvent(PerfTrack.Categories.Compiler, "Interpreted loop compiled");
596 InterpretedFrame frame = (InterpretedFrame)frameObj;
597 var compiler = new LoopCompiler(_loop, frame.Interpreter.LabelMapping, _variables, _closureVariables, _instructionIndex, _loopEnd);
598 var instructions = frame.Interpreter.Instructions.Instructions;
600 // replace this instruction with an optimized one:
601 Interlocked.Exchange(ref instructions[_instructionIndex], new CompiledLoopInstruction(compiler.CreateDelegate()));
603 // invalidate this instruction, some threads may still hold on it:
604 _loop = null;
605 _variables = null;
606 _closureVariables = null;
611 internal sealed class CompiledLoopInstruction : Instruction {
612 private readonly LoopFunc _compiledLoop;
614 public CompiledLoopInstruction(LoopFunc compiledLoop) {
615 Assert.NotNull(compiledLoop);
616 _compiledLoop = compiledLoop;
619 public override int Run(InterpretedFrame frame) {
620 return _compiledLoop(frame.Data, frame.Closure, frame);