Fix XMM scanning on Mac x86.
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / OffsetTrackingILGenerator.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Diagnostics;
19 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Diagnostics.SymbolStore;
22
23 // Not needed in CLR 4 builds because we have the
24 // ILGenerator.ILOffset property.
25
26 #if !FEATURE_CORE_DLR || SILVERLIGHT
27
28 #if !FEATURE_CORE_DLR
29 namespace Microsoft.Scripting.Ast.Compiler {
30 #else
31 namespace System.Linq.Expressions.Compiler {
32 #endif
33     /// <summary>
34     /// Wraps ILGenerator with code that tracks the current IL offset as instructions are emitted into the IL stream.
35     /// </summary>
36     internal sealed class OffsetTrackingILGenerator {
37         private readonly ILGenerator _ilg;
38         internal int _offset;
39
40         internal int ILOffset { get { return _offset; } }
41
42         internal OffsetTrackingILGenerator(ILGenerator ilg) {
43             Debug.Assert(ilg != null);
44             _ilg = ilg;
45         }
46
47         private void AdvanceOffset(OpCode opcode) {
48             _offset += opcode.Size;
49         }
50
51         private void AdvanceOffsetWithLabel(OpCode opcode) {
52             AdvanceOffset(opcode);
53             if (OpCodes.TakesSingleByteArgument(opcode)) {
54                 _offset++;
55             } else {
56                 _offset += 4;
57             }
58         }
59
60         #region Simple Instructions
61
62         internal void Emit(OpCode opcode) {
63             _ilg.Emit(opcode);
64             AdvanceOffset(opcode);
65             AssertOffsetMatches();
66         }
67
68         internal void Emit(OpCode opcode, byte arg) {
69             _ilg.Emit(opcode, arg);
70             AdvanceOffset(opcode);
71             _offset++;
72             AssertOffsetMatches();
73         }
74
75         internal void Emit(OpCode opcode, sbyte arg) {
76             _ilg.Emit(opcode, arg);
77             AdvanceOffset(opcode);
78             _offset++;
79             AssertOffsetMatches();
80         }
81
82         internal void Emit(OpCode opcode, int arg) {
83             _ilg.Emit(opcode, arg);
84             AdvanceOffset(opcode);
85             _offset += 4;
86             AssertOffsetMatches();
87         }
88
89         internal void Emit(OpCode opcode, MethodInfo meth) {
90             _ilg.Emit(opcode, meth);
91             AdvanceOffset(opcode);
92             _offset += 4;
93             AssertOffsetMatches();
94         }
95
96         internal void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes) {
97             _ilg.EmitCall(opcode, methodInfo, optionalParameterTypes);
98             AdvanceOffset(opcode);
99             _offset += 4;
100             AssertOffsetMatches();
101         }
102
103         internal void Emit(OpCode opcode, ConstructorInfo con) {
104             _ilg.Emit(opcode, con);
105             AdvanceOffset(opcode);
106             _offset += 4;
107             AssertOffsetMatches();
108         }
109
110         internal void Emit(OpCode opcode, Type cls) {
111             _ilg.Emit(opcode, cls);
112             AdvanceOffset(opcode);
113             _offset += 4;
114             AssertOffsetMatches();
115         }
116
117         internal void Emit(OpCode opcode, long arg) {
118             _ilg.Emit(opcode, arg);
119             AdvanceOffset(opcode);
120             _offset += 8;
121             AssertOffsetMatches();
122         }
123
124         internal void Emit(OpCode opcode, float arg) {
125             _ilg.Emit(opcode, arg);
126             AdvanceOffset(opcode);
127             _offset += 4;
128             AssertOffsetMatches();
129         }
130
131         internal void Emit(OpCode opcode, double arg) {
132             _ilg.Emit(opcode, arg);
133             AdvanceOffset(opcode);
134             _offset += 8;
135             AssertOffsetMatches();
136         }
137
138         internal void Emit(OpCode opcode, Label label) {
139             _ilg.Emit(opcode, label);
140             AdvanceOffsetWithLabel(opcode);
141             AssertOffsetMatches();
142         }
143
144         internal void Emit(OpCode opcode, Label[] labels) {
145             _ilg.Emit(opcode, labels);
146             AdvanceOffset(opcode);
147             _offset += 4;
148             for (int remaining = labels.Length * 4, i = 0; remaining > 0; remaining -= 4, i++) {
149                 _offset += 4;
150             }
151             AssertOffsetMatches();
152         }
153
154         internal void Emit(OpCode opcode, FieldInfo field) {
155             _ilg.Emit(opcode, field);
156             AdvanceOffset(opcode);
157             _offset += 4;
158             AssertOffsetMatches();
159         }
160
161         internal void Emit(OpCode opcode, String str) {
162             _ilg.Emit(opcode, str);
163             AdvanceOffset(opcode);
164             _offset += 4;
165             AssertOffsetMatches();
166         }
167
168         internal void Emit(OpCode opcode, LocalBuilder local) {
169             _ilg.Emit(opcode, local);
170             int tempVal = local.LocalIndex;
171             if (opcode.Equals(OpCodes.Ldloc)) {
172                 switch (tempVal) {
173                     case 0:
174                         opcode = OpCodes.Ldloc_0;
175                         break;
176                     case 1:
177                         opcode = OpCodes.Ldloc_1;
178                         break;
179                     case 2:
180                         opcode = OpCodes.Ldloc_2;
181                         break;
182                     case 3:
183                         opcode = OpCodes.Ldloc_3;
184                         break;
185                     default:
186                         if (tempVal <= 255)
187                             opcode = OpCodes.Ldloc_S;
188                         break;
189                 }
190             } else if (opcode.Equals(OpCodes.Stloc)) {
191                 switch (tempVal) {
192                     case 0:
193                         opcode = OpCodes.Stloc_0;
194                         break;
195                     case 1:
196                         opcode = OpCodes.Stloc_1;
197                         break;
198                     case 2:
199                         opcode = OpCodes.Stloc_2;
200                         break;
201                     case 3:
202                         opcode = OpCodes.Stloc_3;
203                         break;
204                     default:
205                         if (tempVal <= 255)
206                             opcode = OpCodes.Stloc_S;
207                         break;
208                 }
209             } else if (opcode.Equals(OpCodes.Ldloca)) {
210                 if (tempVal <= 255)
211                     opcode = OpCodes.Ldloca_S;
212             }
213
214             AdvanceOffset(opcode);
215
216             if (opcode.OperandType == OperandType.InlineNone)
217                 return;
218             else if (!OpCodes.TakesSingleByteArgument(opcode)) {
219                 _offset += 2;
220             } else {
221                 _offset++;
222             }
223             AssertOffsetMatches();
224         }
225
226         #endregion
227
228         #region Exception Handling
229         
230         private enum ExceptionState {
231             Try = 0,
232             Filter = 1,
233             Catch = 2,
234             Finally = 3,
235             Fault = 4,
236         }
237
238         private Stack<ExceptionState> _exceptionState = new Stack<ExceptionState>();
239
240         internal void BeginExceptionBlock() {
241             _ilg.BeginExceptionBlock();
242             _exceptionState.Push(ExceptionState.Try);
243             AssertOffsetMatches();
244         }
245
246         internal void EndExceptionBlock() {
247             _ilg.EndExceptionBlock();
248
249             ExceptionState state = _exceptionState.Pop();
250             if (state == ExceptionState.Catch) {
251                 AdvanceOffsetWithLabel(OpCodes.Leave);
252             } else if (state == ExceptionState.Finally || state == ExceptionState.Fault) {
253                 AdvanceOffset(OpCodes.Endfinally);
254             }
255
256             AssertOffsetMatches();
257         }
258
259         internal void BeginExceptFilterBlock() {
260             _ilg.BeginExceptFilterBlock();
261
262             _exceptionState.Pop();
263             _exceptionState.Push(ExceptionState.Filter);
264
265             AssertOffsetMatches();
266         }
267
268         internal void BeginCatchBlock(Type exceptionType) {
269             _ilg.BeginCatchBlock(exceptionType);
270
271             ExceptionState state = _exceptionState.Pop();
272             if (state == ExceptionState.Filter) {
273                 AdvanceOffset(OpCodes.Endfilter);
274             } else {
275                 AdvanceOffsetWithLabel(OpCodes.Leave);
276             }
277
278             _exceptionState.Push(ExceptionState.Catch);
279
280             AssertOffsetMatches();
281         }
282
283         internal void BeginFaultBlock() {
284             _ilg.BeginFaultBlock();
285
286             AdvanceOffsetWithLabel(OpCodes.Leave);
287             _exceptionState.Pop();
288             _exceptionState.Push(ExceptionState.Fault);
289
290             AssertOffsetMatches();
291         }
292
293         internal void BeginFinallyBlock() {
294             _ilg.BeginFinallyBlock();
295
296             ExceptionState state = _exceptionState.Pop();
297             if (state != ExceptionState.Try) {
298                 // leave for any preceeding catch clause
299                 AdvanceOffsetWithLabel(OpCodes.Leave);
300             }
301
302             // leave for try clause                                                  
303             AdvanceOffsetWithLabel(OpCodes.Leave);
304             _exceptionState.Push(ExceptionState.Finally);
305
306             AssertOffsetMatches();
307         }
308
309         #endregion
310
311         #region Labels and Locals
312
313         internal Label DefineLabel() {
314             return _ilg.DefineLabel();
315         }
316
317         internal void MarkLabel(Label loc) {
318             _ilg.MarkLabel(loc);
319         }
320
321         internal LocalBuilder DeclareLocal(Type localType) {
322             return _ilg.DeclareLocal(localType);
323         }
324
325         internal void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) {
326             _ilg.MarkSequencePoint(document, startLine, startColumn, endLine, endColumn);
327         }
328
329         #endregion
330
331         #region Assertions
332
333 #if STRESS_DEBUG
334         private FieldInfo _ilgOffsetField;
335         private bool _checkOffset = true;
336 #endif
337
338         [Conditional("STRESS_DEBUG")]
339         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
340         private void AssertOffsetMatches() {
341 #if STRESS_DEBUG
342             if (!_checkOffset) {
343                 return;
344             }
345
346             int m_length = -1;
347             try {
348                 if (_ilgOffsetField == null) {
349                     _ilgOffsetField = typeof(ILGenerator).GetField("m_length", BindingFlags.NonPublic | BindingFlags.Instance);
350                 }
351                 m_length = (int)_ilgOffsetField.GetValue(_ilg);
352             } catch (Exception) {
353                 _checkOffset = false;
354             }
355
356             if (_checkOffset) {
357                 Debug.Assert(m_length == _offset);
358             }
359 #endif
360         }
361
362         #endregion
363     }
364 }
365
366 #endif