Merge pull request #3422 from xmcclure/tarjan-doublefan
[mono.git] / mcs / tools / tuner / Mono.Tuner / CecilRocks.cs
1 //
2 // MethodBodyRocks.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // Copyright (c) 2008 - 2011 Jb Evain
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.Linq;
32
33 using Mono.Cecil;
34 using Mono.Cecil.Cil;
35
36 namespace Mono.Tuner {
37
38         public static class MethodBodyRocks {
39
40                 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
41                 {
42                         return self.Types.SelectMany (t => t.GetAllTypes ());
43                 }
44
45                 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
46                 {
47                         yield return self;
48
49                         if (!self.HasNestedTypes)
50                                 yield break;
51
52                         foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
53                                 yield return type;
54                 }
55
56                 public static IEnumerable<MethodDefinition> GetMethods (this TypeDefinition self)
57                 {
58                         return self.Methods.Where (m => !m.IsConstructor);
59                 }
60
61                 public static IEnumerable<MethodDefinition> GetConstructors (this TypeDefinition self)
62                 {
63                         return self.Methods.Where (m => m.IsConstructor);
64                 }
65
66                 public static MethodDefinition GetTypeConstructor (this TypeDefinition self)
67                 {
68                         return self.GetConstructors ().FirstOrDefault (c => c.IsStatic);
69                 }
70
71                 public static void SimplifyMacros (this MethodBody self)
72                 {
73                         if (self == null)
74                                 throw new ArgumentNullException ("self");
75
76                         foreach (var instruction in self.Instructions) {
77                                 if (instruction.OpCode.OpCodeType != OpCodeType.Macro)
78                                         continue;
79
80                                 switch (instruction.OpCode.Code) {
81                                 case Code.Ldarg_0:
82                                         ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (0));
83                                         break;
84                                 case Code.Ldarg_1:
85                                         ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (1));
86                                         break;
87                                 case Code.Ldarg_2:
88                                         ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (2));
89                                         break;
90                                 case Code.Ldarg_3:
91                                         ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (3));
92                                         break;
93                                 case Code.Ldloc_0:
94                                         ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [0]);
95                                         break;
96                                 case Code.Ldloc_1:
97                                         ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [1]);
98                                         break;
99                                 case Code.Ldloc_2:
100                                         ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [2]);
101                                         break;
102                                 case Code.Ldloc_3:
103                                         ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [3]);
104                                         break;
105                                 case Code.Stloc_0:
106                                         ExpandMacro (instruction, OpCodes.Stloc, self.Variables [0]);
107                                         break;
108                                 case Code.Stloc_1:
109                                         ExpandMacro (instruction, OpCodes.Stloc, self.Variables [1]);
110                                         break;
111                                 case Code.Stloc_2:
112                                         ExpandMacro (instruction, OpCodes.Stloc, self.Variables [2]);
113                                         break;
114                                 case Code.Stloc_3:
115                                         ExpandMacro (instruction, OpCodes.Stloc, self.Variables [3]);
116                                         break;
117                                 case Code.Ldarg_S:
118                                         instruction.OpCode = OpCodes.Ldarg;
119                                         break;
120                                 case Code.Ldarga_S:
121                                         instruction.OpCode = OpCodes.Ldarga;
122                                         break;
123                                 case Code.Starg_S:
124                                         instruction.OpCode = OpCodes.Starg;
125                                         break;
126                                 case Code.Ldloc_S:
127                                         instruction.OpCode = OpCodes.Ldloc;
128                                         break;
129                                 case Code.Ldloca_S:
130                                         instruction.OpCode = OpCodes.Ldloca;
131                                         break;
132                                 case Code.Stloc_S:
133                                         instruction.OpCode = OpCodes.Stloc;
134                                         break;
135                                 case Code.Ldc_I4_M1:
136                                         ExpandMacro (instruction, OpCodes.Ldc_I4, -1);
137                                         break;
138                                 case Code.Ldc_I4_0:
139                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 0);
140                                         break;
141                                 case Code.Ldc_I4_1:
142                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 1);
143                                         break;
144                                 case Code.Ldc_I4_2:
145                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 2);
146                                         break;
147                                 case Code.Ldc_I4_3:
148                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 3);
149                                         break;
150                                 case Code.Ldc_I4_4:
151                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 4);
152                                         break;
153                                 case Code.Ldc_I4_5:
154                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 5);
155                                         break;
156                                 case Code.Ldc_I4_6:
157                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 6);
158                                         break;
159                                 case Code.Ldc_I4_7:
160                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 7);
161                                         break;
162                                 case Code.Ldc_I4_8:
163                                         ExpandMacro (instruction, OpCodes.Ldc_I4, 8);
164                                         break;
165                                 case Code.Ldc_I4_S:
166                                         ExpandMacro (instruction, OpCodes.Ldc_I4, (int) (sbyte) instruction.Operand);
167                                         break;
168                                 case Code.Br_S:
169                                         instruction.OpCode = OpCodes.Br;
170                                         break;
171                                 case Code.Brfalse_S:
172                                         instruction.OpCode = OpCodes.Brfalse;
173                                         break;
174                                 case Code.Brtrue_S:
175                                         instruction.OpCode = OpCodes.Brtrue;
176                                         break;
177                                 case Code.Beq_S:
178                                         instruction.OpCode = OpCodes.Beq;
179                                         break;
180                                 case Code.Bge_S:
181                                         instruction.OpCode = OpCodes.Bge;
182                                         break;
183                                 case Code.Bgt_S:
184                                         instruction.OpCode = OpCodes.Bgt;
185                                         break;
186                                 case Code.Ble_S:
187                                         instruction.OpCode = OpCodes.Ble;
188                                         break;
189                                 case Code.Blt_S:
190                                         instruction.OpCode = OpCodes.Blt;
191                                         break;
192                                 case Code.Bne_Un_S:
193                                         instruction.OpCode = OpCodes.Bne_Un;
194                                         break;
195                                 case Code.Bge_Un_S:
196                                         instruction.OpCode = OpCodes.Bge_Un;
197                                         break;
198                                 case Code.Bgt_Un_S:
199                                         instruction.OpCode = OpCodes.Bgt_Un;
200                                         break;
201                                 case Code.Ble_Un_S:
202                                         instruction.OpCode = OpCodes.Ble_Un;
203                                         break;
204                                 case Code.Blt_Un_S:
205                                         instruction.OpCode = OpCodes.Blt_Un;
206                                         break;
207                                 case Code.Leave_S:
208                                         instruction.OpCode = OpCodes.Leave;
209                                         break;
210                                 }
211                         }
212                 }
213
214                 static void ExpandMacro (Instruction instruction, OpCode opcode, object operand)
215                 {
216                         instruction.OpCode = opcode;
217                         instruction.Operand = operand;
218                 }
219
220                 static void MakeMacro (Instruction instruction, OpCode opcode)
221                 {
222                         instruction.OpCode = opcode;
223                         instruction.Operand = null;
224                 }
225
226                 public static void OptimizeMacros (this MethodBody self)
227                 {
228                         if (self == null)
229                                 throw new ArgumentNullException ("self");
230
231                         var method = self.Method;
232
233                         foreach (var instruction in self.Instructions) {
234                                 int index;
235                                 switch (instruction.OpCode.Code) {
236                                 case Code.Ldarg:
237                                         index = ((ParameterDefinition) instruction.Operand).Index;
238                                         if (index == -1 && instruction.Operand == self.ThisParameter)
239                                                 index = 0;
240                                         else if (method.HasThis)
241                                                 index++;
242
243                                         switch (index) {
244                                         case 0:
245                                                 MakeMacro (instruction, OpCodes.Ldarg_0);
246                                                 break;
247                                         case 1:
248                                                 MakeMacro (instruction, OpCodes.Ldarg_1);
249                                                 break;
250                                         case 2:
251                                                 MakeMacro (instruction, OpCodes.Ldarg_2);
252                                                 break;
253                                         case 3:
254                                                 MakeMacro (instruction, OpCodes.Ldarg_3);
255                                                 break;
256                                         default:
257                                                 if (index < 256)
258                                                         ExpandMacro (instruction, OpCodes.Ldarg_S, instruction.Operand);
259                                                 break;
260                                         }
261                                         break;
262                                 case Code.Ldloc:
263                                         index = ((VariableDefinition) instruction.Operand).Index;
264                                         switch (index) {
265                                         case 0:
266                                                 MakeMacro (instruction, OpCodes.Ldloc_0);
267                                                 break;
268                                         case 1:
269                                                 MakeMacro (instruction, OpCodes.Ldloc_1);
270                                                 break;
271                                         case 2:
272                                                 MakeMacro (instruction, OpCodes.Ldloc_2);
273                                                 break;
274                                         case 3:
275                                                 MakeMacro (instruction, OpCodes.Ldloc_3);
276                                                 break;
277                                         default:
278                                                 if (index < 256)
279                                                         ExpandMacro (instruction, OpCodes.Ldloc_S, instruction.Operand);
280                                                 break;
281                                         }
282                                         break;
283                                 case Code.Stloc:
284                                         index = ((VariableDefinition) instruction.Operand).Index;
285                                         switch (index) {
286                                         case 0:
287                                                 MakeMacro (instruction, OpCodes.Stloc_0);
288                                                 break;
289                                         case 1:
290                                                 MakeMacro (instruction, OpCodes.Stloc_1);
291                                                 break;
292                                         case 2:
293                                                 MakeMacro (instruction, OpCodes.Stloc_2);
294                                                 break;
295                                         case 3:
296                                                 MakeMacro (instruction, OpCodes.Stloc_3);
297                                                 break;
298                                         default:
299                                                 if (index < 256)
300                                                         ExpandMacro (instruction, OpCodes.Stloc_S, instruction.Operand);
301                                                 break;
302                                         }
303                                         break;
304                                 case Code.Ldarga:
305                                         index = ((ParameterDefinition) instruction.Operand).Index;
306                                         if (index == -1 && instruction.Operand == self.ThisParameter)
307                                                 index = 0;
308                                         else if (method.HasThis)
309                                                 index++;
310                                         if (index < 256)
311                                                 ExpandMacro (instruction, OpCodes.Ldarga_S, instruction.Operand);
312                                         break;
313                                 case Code.Ldloca:
314                                         if (((VariableDefinition) instruction.Operand).Index < 256)
315                                                 ExpandMacro (instruction, OpCodes.Ldloca_S, instruction.Operand);
316                                         break;
317                                 case Code.Ldc_I4:
318                                         int i = (int) instruction.Operand;
319                                         switch (i) {
320                                         case -1:
321                                                 MakeMacro (instruction, OpCodes.Ldc_I4_M1);
322                                                 break;
323                                         case 0:
324                                                 MakeMacro (instruction, OpCodes.Ldc_I4_0);
325                                                 break;
326                                         case 1:
327                                                 MakeMacro (instruction, OpCodes.Ldc_I4_1);
328                                                 break;
329                                         case 2:
330                                                 MakeMacro (instruction, OpCodes.Ldc_I4_2);
331                                                 break;
332                                         case 3:
333                                                 MakeMacro (instruction, OpCodes.Ldc_I4_3);
334                                                 break;
335                                         case 4:
336                                                 MakeMacro (instruction, OpCodes.Ldc_I4_4);
337                                                 break;
338                                         case 5:
339                                                 MakeMacro (instruction, OpCodes.Ldc_I4_5);
340                                                 break;
341                                         case 6:
342                                                 MakeMacro (instruction, OpCodes.Ldc_I4_6);
343                                                 break;
344                                         case 7:
345                                                 MakeMacro (instruction, OpCodes.Ldc_I4_7);
346                                                 break;
347                                         case 8:
348                                                 MakeMacro (instruction, OpCodes.Ldc_I4_8);
349                                                 break;
350                                         default:
351                                                 if (i >= -128 && i < 128)
352                                                         ExpandMacro (instruction, OpCodes.Ldc_I4_S, (sbyte) i);
353                                                 break;
354                                         }
355                                         break;
356                                 }
357                         }
358
359                         OptimizeBranches (self);
360                 }
361
362                 static void OptimizeBranches (MethodBody body)
363                 {
364                         ComputeOffsets (body);
365
366                         foreach (var instruction in body.Instructions) {
367                                 if (instruction.OpCode.OperandType != OperandType.InlineBrTarget)
368                                         continue;
369
370                                 if (OptimizeBranch (instruction))
371                                         ComputeOffsets (body);
372                         }
373                 }
374
375                 static bool OptimizeBranch (Instruction instruction)
376                 {
377                         var offset = ((Instruction) instruction.Operand).Offset - (instruction.Offset + instruction.OpCode.Size + 4);
378                         if (!(offset >= -128 && offset <= 127))
379                                 return false;
380
381                         switch (instruction.OpCode.Code) {
382                         case Code.Br:
383                                 instruction.OpCode = OpCodes.Br_S;
384                                 break;
385                         case Code.Brfalse:
386                                 instruction.OpCode = OpCodes.Brfalse_S;
387                                 break;
388                         case Code.Brtrue:
389                                 instruction.OpCode = OpCodes.Brtrue_S;
390                                 break;
391                         case Code.Beq:
392                                 instruction.OpCode = OpCodes.Beq_S;
393                                 break;
394                         case Code.Bge:
395                                 instruction.OpCode = OpCodes.Bge_S;
396                                 break;
397                         case Code.Bgt:
398                                 instruction.OpCode = OpCodes.Bgt_S;
399                                 break;
400                         case Code.Ble:
401                                 instruction.OpCode = OpCodes.Ble_S;
402                                 break;
403                         case Code.Blt:
404                                 instruction.OpCode = OpCodes.Blt_S;
405                                 break;
406                         case Code.Bne_Un:
407                                 instruction.OpCode = OpCodes.Bne_Un_S;
408                                 break;
409                         case Code.Bge_Un:
410                                 instruction.OpCode = OpCodes.Bge_Un_S;
411                                 break;
412                         case Code.Bgt_Un:
413                                 instruction.OpCode = OpCodes.Bgt_Un_S;
414                                 break;
415                         case Code.Ble_Un:
416                                 instruction.OpCode = OpCodes.Ble_Un_S;
417                                 break;
418                         case Code.Blt_Un:
419                                 instruction.OpCode = OpCodes.Blt_Un_S;
420                                 break;
421                         case Code.Leave:
422                                 instruction.OpCode = OpCodes.Leave_S;
423                                 break;
424                         }
425
426                         return true;
427                 }
428
429                 static void ComputeOffsets (MethodBody body)
430                 {
431                         var offset = 0;
432                         foreach (var instruction in body.Instructions) {
433                                 instruction.Offset = offset;
434                                 offset += instruction.GetSize ();
435                         }
436                 }
437
438                 public static ParameterDefinition GetParameter (this MethodBody self, int index)
439                 {
440                         var method = self.Method;
441
442                         if (method.HasThis) {
443                                 if (index == 0)
444                                         return self.ThisParameter;
445
446                                 index--;
447                         }
448
449                         var parameters = method.Parameters;
450
451                         if (index < 0 || index >= parameters.Count)
452                                 return null;
453
454                         return parameters [index];
455                 }
456
457                 public static bool Implements (this TypeReference self, string interfaceName)
458                 {
459                         if (interfaceName == null)
460                                 throw new ArgumentNullException ("interfaceName");
461                         if (self == null)
462                                 return false;
463
464                         TypeDefinition type = self.Resolve ();
465                         if (type == null)
466                                 return false;   // not enough information available
467
468                         // special case, check if we implement ourselves
469                         if (type.IsInterface && (type.FullName == interfaceName))
470                                 return true;
471
472                         return Implements (type, interfaceName, (interfaceName.IndexOf ('`') >= 0));
473                 }
474
475                 public static bool Implements (TypeDefinition type, string interfaceName, bool generic)
476                 {
477                         while (type != null) {
478                                 // does the type implements it itself
479                                 if (type.HasInterfaces) {
480                                         foreach (var iface in type.Interfaces) {
481                                                 string fullname = (generic) ? iface.InterfaceType.GetElementType ().FullName : iface.InterfaceType.FullName;
482                                                 if (fullname == interfaceName)
483                                                         return true;
484                                                 //if not, then maybe one of its parent interfaces does
485                                                 if (Implements (iface.InterfaceType.Resolve (), interfaceName, generic))
486                                                         return true;
487                                         }
488                                 }
489
490                                 type = type.BaseType != null ? type.BaseType.Resolve () : null;
491                         }
492                         return false;
493                 }
494
495                 public static bool Inherits (this TypeReference self, string @namespace, string name)
496                 {
497                         if (@namespace == null)
498                                 throw new ArgumentNullException ("namespace");
499                         if (name == null)
500                                 throw new ArgumentNullException ("name");
501                         if (self == null)
502                                 return false;
503                         
504                         TypeReference current = self.Resolve ();
505                         while (current != null) {
506                                 if (current.Is (@namespace, name))
507                                         return true;
508                                 if (current.Is ("System", "Object"))
509                                         return false;
510                                 
511                                 TypeDefinition td = current.Resolve ();
512                                 if (td == null)
513                                         return false;           // could not resolve type
514                                 current = td.BaseType;
515                         }
516                         return false;
517                 }
518         }
519 }