Replace SIZEOF_REGISTER with sizeof(mgreg_t) for consistency with sizeof(gpointer)
[mono.git] / mcs / mcs / codegen.cs
1 //
2 // codegen.cs: The code generator
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004 Novell, Inc.
10 //
11
12 using System;
13 using System.Collections.Generic;
14
15 #if STATIC
16 using MetaType = IKVM.Reflection.Type;
17 using IKVM.Reflection;
18 using IKVM.Reflection.Emit;
19 #else
20 using MetaType = System.Type;
21 using System.Reflection;
22 using System.Reflection.Emit;
23 #endif
24
25 namespace Mono.CSharp
26 {
27         /// <summary>
28         ///   An Emit Context is created for each body of code (from methods,
29         ///   properties bodies, indexer bodies or constructor bodies)
30         /// </summary>
31         public class EmitContext : BuilderContext
32         {
33                 // TODO: Has to be private
34                 public ILGenerator ig;
35
36                 /// <summary>
37                 ///   The value that is allowed to be returned or NULL if there is no
38                 ///   return type.
39                 /// </summary>
40                 TypeSpec return_type;
41
42                 /// <summary>
43                 ///   Keeps track of the Type to LocalBuilder temporary storage created
44                 ///   to store structures (used to compute the address of the structure
45                 ///   value on structure method invocations)
46                 /// </summary>
47                 Dictionary<TypeSpec, object> temporary_storage;
48
49                 /// <summary>
50                 ///   The location where we store the return value.
51                 /// </summary>
52                 public LocalBuilder return_value;
53
54                 /// <summary>
55                 ///   The location where return has to jump to return the
56                 ///   value
57                 /// </summary>
58                 public Label ReturnLabel;
59
60                 /// <summary>
61                 ///   If we already defined the ReturnLabel
62                 /// </summary>
63                 public bool HasReturnLabel;
64
65                 /// <summary>
66                 ///   Current loop begin and end labels.
67                 /// </summary>
68                 public Label LoopBegin, LoopEnd;
69
70                 /// <summary>
71                 ///   Default target in a switch statement.   Only valid if
72                 ///   InSwitch is true
73                 /// </summary>
74                 public Label DefaultTarget;
75
76                 /// <summary>
77                 ///   If this is non-null, points to the current switch statement
78                 /// </summary>
79                 public Switch Switch;
80
81                 /// <summary>
82                 ///  Whether we are inside an anonymous method.
83                 /// </summary>
84                 public AnonymousExpression CurrentAnonymousMethod;
85                 
86                 public readonly IMemberContext MemberContext;
87
88                 DynamicSiteClass dynamic_site_container;
89
90                 // TODO: Replace IMemberContext with MemberCore
91                 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
92                 {
93                         this.MemberContext = rc;
94                         this.ig = ig;
95
96                         this.return_type = return_type;
97
98 #if STATIC
99                         ig.__CleverExceptionBlockAssistance ();
100 #endif
101                 }
102
103                 #region Properties
104
105                 public TypeSpec CurrentType {
106                         get { return MemberContext.CurrentType; }
107                 }
108
109                 public TypeParameter[] CurrentTypeParameters {
110                         get { return MemberContext.CurrentTypeParameters; }
111                 }
112
113                 public MemberCore CurrentTypeDefinition {
114                         get { return MemberContext.CurrentMemberDefinition; }
115                 }
116
117                 public bool IsStatic {
118                         get { return MemberContext.IsStatic; }
119                 }
120
121                 bool IsAnonymousStoreyMutateRequired {
122                         get {
123                                 return CurrentAnonymousMethod != null &&
124                                         CurrentAnonymousMethod.Storey != null &&
125                                         CurrentAnonymousMethod.Storey.Mutator != null;
126                         }
127                 }
128
129                 // Has to be used for emitter errors only
130                 public Report Report {
131                         get { return MemberContext.Compiler.Report; }
132                 }
133
134                 public TypeSpec ReturnType {
135                         get {
136                                 return return_type;
137                         }
138                 }
139                 #endregion
140
141                 /// <summary>
142                 ///   This is called immediately before emitting an IL opcode to tell the symbol
143                 ///   writer to which source line this opcode belongs.
144                 /// </summary>
145                 public void Mark (Location loc)
146                 {
147                         if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
148                                 return;
149
150                         SymbolWriter.MarkSequencePoint (ig, loc);
151                 }
152
153                 public void DefineLocalVariable (string name, LocalBuilder builder)
154                 {
155                         SymbolWriter.DefineLocalVariable (name, builder);
156                 }
157
158                 public void BeginCatchBlock (TypeSpec type)
159                 {
160                         ig.BeginCatchBlock (type.GetMetaInfo ());
161                 }
162
163                 public void BeginExceptionBlock ()
164                 {
165                         ig.BeginExceptionBlock ();
166                 }
167
168                 public void BeginFinallyBlock ()
169                 {
170                         ig.BeginFinallyBlock ();
171                 }
172
173                 public void BeginScope ()
174                 {
175                         SymbolWriter.OpenScope(ig);
176                 }
177
178                 public void EndExceptionBlock ()
179                 {
180                         ig.EndExceptionBlock ();
181                 }
182
183                 public void EndScope ()
184                 {
185                         SymbolWriter.CloseScope(ig);
186                 }
187
188                 //
189                 // Creates a nested container in this context for all dynamic compiler generated stuff
190                 //
191                 public DynamicSiteClass CreateDynamicSite ()
192                 {
193                         if (dynamic_site_container == null) {
194                                 var mc = MemberContext.CurrentMemberDefinition as MemberBase;
195                                 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, CurrentTypeParameters);
196
197                                 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
198                                 dynamic_site_container.CreateType ();
199                                 dynamic_site_container.DefineType ();
200                                 dynamic_site_container.ResolveTypeParameters ();
201                                 dynamic_site_container.Define ();
202                         }
203
204                         return dynamic_site_container;
205                 }
206
207                 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
208                 {
209                         if (IsAnonymousStoreyMutateRequired)
210                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
211
212                         return ig.DeclareLocal (type.GetMetaInfo (), pinned);
213                 }
214
215                 public Label DefineLabel ()
216                 {
217                         return ig.DefineLabel ();
218                 }
219
220                 public void MarkLabel (Label label)
221                 {
222                         ig.MarkLabel (label);
223                 }
224
225                 public void Emit (OpCode opcode)
226                 {
227                         ig.Emit (opcode);
228                 }
229
230                 public void Emit (OpCode opcode, LocalBuilder local)
231                 {
232                         ig.Emit (opcode, local);
233                 }
234
235                 public void Emit (OpCode opcode, string arg)
236                 {
237                         ig.Emit (opcode, arg);
238                 }
239
240                 public void Emit (OpCode opcode, double arg)
241                 {
242                         ig.Emit (opcode, arg);
243                 }
244
245                 public void Emit (OpCode opcode, float arg)
246                 {
247                         ig.Emit (opcode, arg);
248                 }
249
250                 public void Emit (OpCode opcode, int arg)
251                 {
252                         ig.Emit (opcode, arg);
253                 }
254
255                 public void Emit (OpCode opcode, byte arg)
256                 {
257                         ig.Emit (opcode, arg);
258                 }
259
260                 public void Emit (OpCode opcode, Label label)
261                 {
262                         ig.Emit (opcode, label);
263                 }
264
265                 public void Emit (OpCode opcode, Label[] labels)
266                 {
267                         ig.Emit (opcode, labels);
268                 }
269
270                 public void Emit (OpCode opcode, TypeSpec type)
271                 {
272                         if (IsAnonymousStoreyMutateRequired)
273                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
274
275                         ig.Emit (opcode, type.GetMetaInfo ());
276                 }
277
278                 public void Emit (OpCode opcode, FieldSpec field)
279                 {
280                         if (IsAnonymousStoreyMutateRequired)
281                                 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
282
283                         ig.Emit (opcode, field.GetMetaInfo ());
284                 }
285
286                 public void Emit (OpCode opcode, MethodSpec method)
287                 {
288                         if (IsAnonymousStoreyMutateRequired)
289                                 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
290
291                         if (method.IsConstructor)
292                                 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
293                         else
294                                 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
295                 }
296
297                 // TODO: REMOVE breaks mutator
298                 public void Emit (OpCode opcode, MethodInfo method)
299                 {
300                         ig.Emit (opcode, method);
301                 }
302
303                 // TODO: REMOVE breaks mutator
304                 public void Emit (OpCode opcode, FieldBuilder field)
305                 {
306                         ig.Emit (opcode, field);
307                 }
308
309                 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
310                 {
311                         // TODO MemberCache: This should mutate too
312                         ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
313                 }
314
315                 public void EmitArrayNew (ArrayContainer ac)
316                 {
317                         if (ac.Rank == 1) {
318                                 Emit (OpCodes.Newarr, ac.Element);
319                         } else {
320                                 if (IsAnonymousStoreyMutateRequired)
321                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
322
323                                 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
324                         }
325                 }
326
327                 public void EmitArrayAddress (ArrayContainer ac)
328                 {
329                         if (ac.Element.IsGenericParameter)
330                                 ig.Emit (OpCodes.Readonly);
331
332                         if (ac.Rank > 1) {
333                                 if (IsAnonymousStoreyMutateRequired)
334                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
335
336                                 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
337                         } else {
338                                 Emit (OpCodes.Ldelema, ac.Element);
339                         }
340                 }
341
342                 //
343                 // Emits the right opcode to load from an array
344                 //
345                 public void EmitArrayLoad (ArrayContainer ac)
346                 {
347                         if (ac.Rank > 1) {
348                                 if (IsAnonymousStoreyMutateRequired)
349                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
350
351                                 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
352                                 return;
353                         }
354
355                         var type = ac.Element;
356                         if (TypeManager.IsEnumType (type))
357                                 type = EnumSpec.GetUnderlyingType (type);
358
359                         if (type == TypeManager.byte_type || type == TypeManager.bool_type)
360                                 Emit (OpCodes.Ldelem_U1);
361                         else if (type == TypeManager.sbyte_type)
362                                 Emit (OpCodes.Ldelem_I1);
363                         else if (type == TypeManager.short_type)
364                                 Emit (OpCodes.Ldelem_I2);
365                         else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
366                                 Emit (OpCodes.Ldelem_U2);
367                         else if (type == TypeManager.int32_type)
368                                 Emit (OpCodes.Ldelem_I4);
369                         else if (type == TypeManager.uint32_type)
370                                 Emit (OpCodes.Ldelem_U4);
371                         else if (type == TypeManager.uint64_type)
372                                 Emit (OpCodes.Ldelem_I8);
373                         else if (type == TypeManager.int64_type)
374                                 Emit (OpCodes.Ldelem_I8);
375                         else if (type == TypeManager.float_type)
376                                 Emit (OpCodes.Ldelem_R4);
377                         else if (type == TypeManager.double_type)
378                                 Emit (OpCodes.Ldelem_R8);
379                         else if (type == TypeManager.intptr_type)
380                                 Emit (OpCodes.Ldelem_I);
381                         else if (TypeManager.IsStruct (type)) {
382                                 Emit (OpCodes.Ldelema, type);
383                                 Emit (OpCodes.Ldobj, type);
384                         } else if (type.IsGenericParameter) {
385                                 Emit (OpCodes.Ldelem, type);
386                         } else if (type.IsPointer)
387                                 Emit (OpCodes.Ldelem_I);
388                         else
389                                 Emit (OpCodes.Ldelem_Ref);
390                 }
391
392                 //
393                 // Emits the right opcode to store to an array
394                 //
395                 public void EmitArrayStore (ArrayContainer ac)
396                 {
397                         if (ac.Rank > 1) {
398                                 if (IsAnonymousStoreyMutateRequired)
399                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
400
401                                 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
402                                 return;
403                         }
404
405                         var type = ac.Element;
406
407                         if (type.IsEnum)
408                                 type = EnumSpec.GetUnderlyingType (type);
409
410                         if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || type == TypeManager.bool_type)
411                                 Emit (OpCodes.Stelem_I1);
412                         else if (type == TypeManager.short_type || type == TypeManager.ushort_type || type == TypeManager.char_type)
413                                 Emit (OpCodes.Stelem_I2);
414                         else if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
415                                 Emit (OpCodes.Stelem_I4);
416                         else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
417                                 Emit (OpCodes.Stelem_I8);
418                         else if (type == TypeManager.float_type)
419                                 Emit (OpCodes.Stelem_R4);
420                         else if (type == TypeManager.double_type)
421                                 Emit (OpCodes.Stelem_R8);
422                         else if (type == TypeManager.intptr_type)
423                                 Emit (OpCodes.Stobj, type);
424                         else if (TypeManager.IsStruct (type))
425                                 Emit (OpCodes.Stobj, type);
426                         else if (type.IsGenericParameter)
427                                 Emit (OpCodes.Stelem, type);
428                         else if (type.IsPointer)
429                                 Emit (OpCodes.Stelem_I);
430                         else
431                                 Emit (OpCodes.Stelem_Ref);
432                 }
433
434                 public void EmitInt (int i)
435                 {
436                         switch (i) {
437                         case -1:
438                                 ig.Emit (OpCodes.Ldc_I4_M1);
439                                 break;
440
441                         case 0:
442                                 ig.Emit (OpCodes.Ldc_I4_0);
443                                 break;
444
445                         case 1:
446                                 ig.Emit (OpCodes.Ldc_I4_1);
447                                 break;
448
449                         case 2:
450                                 ig.Emit (OpCodes.Ldc_I4_2);
451                                 break;
452
453                         case 3:
454                                 ig.Emit (OpCodes.Ldc_I4_3);
455                                 break;
456
457                         case 4:
458                                 ig.Emit (OpCodes.Ldc_I4_4);
459                                 break;
460
461                         case 5:
462                                 ig.Emit (OpCodes.Ldc_I4_5);
463                                 break;
464
465                         case 6:
466                                 ig.Emit (OpCodes.Ldc_I4_6);
467                                 break;
468
469                         case 7:
470                                 ig.Emit (OpCodes.Ldc_I4_7);
471                                 break;
472
473                         case 8:
474                                 ig.Emit (OpCodes.Ldc_I4_8);
475                                 break;
476
477                         default:
478                                 if (i >= -128 && i <= 127) {
479                                         ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
480                                 } else
481                                         ig.Emit (OpCodes.Ldc_I4, i);
482                                 break;
483                         }
484                 }
485
486                 public void EmitLong (long l)
487                 {
488                         if (l >= int.MinValue && l <= int.MaxValue) {
489                                 EmitInt (unchecked ((int) l));
490                                 ig.Emit (OpCodes.Conv_I8);
491                                 return;
492                         }
493
494                         if (l >= 0 && l <= uint.MaxValue) {
495                                 EmitInt (unchecked ((int) l));
496                                 ig.Emit (OpCodes.Conv_U8);
497                                 return;
498                         }
499
500                         ig.Emit (OpCodes.Ldc_I8, l);
501                 }
502
503                 //
504                 // Load the object from the pointer.  
505                 //
506                 public void EmitLoadFromPtr (TypeSpec t)
507                 {
508                         if (t == TypeManager.int32_type)
509                                 ig.Emit (OpCodes.Ldind_I4);
510                         else if (t == TypeManager.uint32_type)
511                                 ig.Emit (OpCodes.Ldind_U4);
512                         else if (t == TypeManager.short_type)
513                                 ig.Emit (OpCodes.Ldind_I2);
514                         else if (t == TypeManager.ushort_type)
515                                 ig.Emit (OpCodes.Ldind_U2);
516                         else if (t == TypeManager.char_type)
517                                 ig.Emit (OpCodes.Ldind_U2);
518                         else if (t == TypeManager.byte_type)
519                                 ig.Emit (OpCodes.Ldind_U1);
520                         else if (t == TypeManager.sbyte_type)
521                                 ig.Emit (OpCodes.Ldind_I1);
522                         else if (t == TypeManager.uint64_type)
523                                 ig.Emit (OpCodes.Ldind_I8);
524                         else if (t == TypeManager.int64_type)
525                                 ig.Emit (OpCodes.Ldind_I8);
526                         else if (t == TypeManager.float_type)
527                                 ig.Emit (OpCodes.Ldind_R4);
528                         else if (t == TypeManager.double_type)
529                                 ig.Emit (OpCodes.Ldind_R8);
530                         else if (t == TypeManager.bool_type)
531                                 ig.Emit (OpCodes.Ldind_I1);
532                         else if (t == TypeManager.intptr_type)
533                                 ig.Emit (OpCodes.Ldind_I);
534                         else if (t.IsEnum) {
535                                 if (t == TypeManager.enum_type)
536                                         ig.Emit (OpCodes.Ldind_Ref);
537                                 else
538                                         EmitLoadFromPtr (EnumSpec.GetUnderlyingType (t));
539                         } else if (TypeManager.IsStruct (t) || TypeManager.IsGenericParameter (t))
540                                 Emit (OpCodes.Ldobj, t);
541                         else if (t.IsPointer)
542                                 ig.Emit (OpCodes.Ldind_I);
543                         else
544                                 ig.Emit (OpCodes.Ldind_Ref);
545                 }
546
547                 //
548                 // The stack contains the pointer and the value of type `type'
549                 //
550                 public void EmitStoreFromPtr (TypeSpec type)
551                 {
552                         if (type.IsEnum)
553                                 type = EnumSpec.GetUnderlyingType (type);
554
555                         if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
556                                 ig.Emit (OpCodes.Stind_I4);
557                         else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
558                                 ig.Emit (OpCodes.Stind_I8);
559                         else if (type == TypeManager.char_type || type == TypeManager.short_type ||
560                                  type == TypeManager.ushort_type)
561                                 ig.Emit (OpCodes.Stind_I2);
562                         else if (type == TypeManager.float_type)
563                                 ig.Emit (OpCodes.Stind_R4);
564                         else if (type == TypeManager.double_type)
565                                 ig.Emit (OpCodes.Stind_R8);
566                         else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type ||
567                                  type == TypeManager.bool_type)
568                                 ig.Emit (OpCodes.Stind_I1);
569                         else if (type == TypeManager.intptr_type)
570                                 ig.Emit (OpCodes.Stind_I);
571                         else if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
572                                 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
573                         else
574                                 ig.Emit (OpCodes.Stind_Ref);
575                 }
576
577                 /// <summary>
578                 ///   Returns a temporary storage for a variable of type t as 
579                 ///   a local variable in the current body.
580                 /// </summary>
581                 public LocalBuilder GetTemporaryLocal (TypeSpec t)
582                 {
583                         if (temporary_storage != null) {
584                                 object o;
585                                 if (temporary_storage.TryGetValue (t, out o)) {
586                                         if (o is Stack<LocalBuilder>) {
587                                                 var s = (Stack<LocalBuilder>) o;
588                                                 o = s.Count == 0 ? null : s.Pop ();
589                                         } else {
590                                                 temporary_storage.Remove (t);
591                                         }
592                                 }
593                                 if (o != null)
594                                         return (LocalBuilder) o;
595                         }
596                         return DeclareLocal (t, false);
597                 }
598
599                 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
600                 {
601                         if (temporary_storage == null) {
602                                 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
603                                 temporary_storage.Add (t, b);
604                                 return;
605                         }
606                         object o;
607                         
608                         if (!temporary_storage.TryGetValue (t, out o)) {
609                                 temporary_storage.Add (t, b);
610                                 return;
611                         }
612                         var s = o as Stack<LocalBuilder>;
613                         if (s == null) {
614                                 s = new Stack<LocalBuilder> ();
615                                 s.Push ((LocalBuilder)o);
616                                 temporary_storage [t] = s;
617                         }
618                         s.Push (b);
619                 }
620
621                 /// <summary>
622                 ///   ReturnValue creates on demand the LocalBuilder for the
623                 ///   return value from the function.  By default this is not
624                 ///   used.  This is only required when returns are found inside
625                 ///   Try or Catch statements.
626                 ///
627                 ///   This method is typically invoked from the Emit phase, so
628                 ///   we allow the creation of a return label if it was not
629                 ///   requested during the resolution phase.   Could be cleaned
630                 ///   up, but it would replicate a lot of logic in the Emit phase
631                 ///   of the code that uses it.
632                 /// </summary>
633                 public LocalBuilder TemporaryReturn ()
634                 {
635                         if (return_value == null){
636                                 return_value = DeclareLocal (return_type, false);
637                                 if (!HasReturnLabel){
638                                         ReturnLabel = DefineLabel ();
639                                         HasReturnLabel = true;
640                                 }
641                         }
642
643                         return return_value;
644                 }
645         }
646 }