Welcome to mirror list, hosted at ThFree Co, Russian Federation.

CompilerTypeSystemContext.BoxedTypes.cs « Compiler « src « ILCompiler.Compiler « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 71b9720fc1b2c26f6e134ce9a2f0effd5af94d7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Text;

using Internal.TypeSystem;
using Internal.IL;
using Internal.IL.Stubs;

using Debug = System.Diagnostics.Debug;

//
// Functionality related to instantiating unboxing thunks
//
// To support calling canonical interface methods on generic valuetypes,
// the compiler needs to generate unboxing+instantiating thunks that bridge
// the difference between the two calling conventions.
//
// As a refresher:
// * Instance methods on shared generic valuetypes expect two arguments
//   (aside from the arguments declared in the signature): a ByRef to the
//   first byte of the value of the valuetype (this), and a generic context
//   argument (EEType)
// * Interface calls expect 'this' to be a reference type (with the generic
//   context to be inferred from 'this' by the callee).
//
// Instantiating and unboxing stubs bridge this by extracting a managed
// pointer out of a boxed valuetype, along with the EEType of the boxed
// valuetype (to provide the generic context) before dispatching to the
// instance method with the different calling convention.
//
// We compile them by:
// * Pretending the unboxing stub is an instance method on a reference type
//   with the same layout as a boxed valuetype (this matches the calling
//   convention expected by the caller).
// * Having the unboxing stub load the m_pEEType field (to get generic
//   context) and a byref to the actual value (to get a 'this' expected by
//   valuetype methods)
// * Generating a call to a fake instance method on the valuetype that has
//   the hidden (generic context) argument explicitly present in the
//   signature. We need a fake method to be able to refer to the hidden parameter
//   from IL.
//
// At a later stage (once codegen is done), we replace the references to the
// fake instance method with the real instance method. Their signatures after
// compilation is identical.
//

namespace ILCompiler
{
    // Contains functionality related to pseudotypes representing boxed instances of value types
    partial class CompilerTypeSystemContext
    {
        /// <summary>
        /// For a shared (canonical) instance method on a generic valuetype, gets a method that can be used to call the
        /// method given a boxed version of the generic valuetype as 'this' pointer.
        /// </summary>
        public MethodDesc GetSpecialUnboxingThunk(MethodDesc targetMethod, ModuleDesc ownerModuleOfThunk)
        {
            Debug.Assert(targetMethod.IsSharedByGenericInstantiations);
            Debug.Assert(!targetMethod.Signature.IsStatic);
            Debug.Assert(!targetMethod.HasInstantiation);

            TypeDesc owningType = targetMethod.OwningType;
            Debug.Assert(owningType.IsValueType);

            var owningTypeDefinition = (MetadataType)owningType.GetTypeDefinition();

            // Get a reference type that has the same layout as the boxed valuetype.
            var typeKey = new BoxedValuetypeHashtableKey(owningTypeDefinition, ownerModuleOfThunk);
            BoxedValueType boxedTypeDefinition = _boxedValuetypeHashtable.GetOrCreateValue(typeKey);

            // Get a method on the reference type with the same signature as the target method (but different
            // calling convention, since 'this' will be a reference type).
            var targetMethodDefinition = targetMethod.GetTypicalMethodDefinition();
            var methodKey = new UnboxingThunkHashtableKey(targetMethodDefinition, boxedTypeDefinition);
            GenericUnboxingThunk thunkDefinition = _unboxingThunkHashtable.GetOrCreateValue(methodKey);

            // Find the thunk on the instantiated version of the reference type.
            Debug.Assert(owningType != owningTypeDefinition);
            InstantiatedType boxedType = boxedTypeDefinition.MakeInstantiatedType(owningType.Instantiation);

            MethodDesc thunk = GetMethodForInstantiatedType(thunkDefinition, boxedType);
            Debug.Assert(!thunk.HasInstantiation);

            return thunk;
        }

        public MethodDesc GetUnboxingThunk(MethodDesc targetMethod, ModuleDesc ownerModuleOfThunk)
        {
            TypeDesc owningType = targetMethod.OwningType;
            Debug.Assert(owningType.IsValueType);

            var owningTypeDefinition = (MetadataType)owningType.GetTypeDefinition();

            // Get a reference type that has the same layout as the boxed valuetype.
            var typeKey = new BoxedValuetypeHashtableKey(owningTypeDefinition, ownerModuleOfThunk);
            BoxedValueType boxedTypeDefinition = _boxedValuetypeHashtable.GetOrCreateValue(typeKey);

            // Get a method on the reference type with the same signature as the target method (but different
            // calling convention, since 'this' will be a reference type).
            var targetMethodDefinition = targetMethod.GetTypicalMethodDefinition();
            var methodKey = new UnboxingThunkHashtableKey(targetMethodDefinition, boxedTypeDefinition);
            UnboxingThunk thunkDefinition = _nonGenericUnboxingThunkHashtable.GetOrCreateValue(methodKey);

            // Find the thunk on the instantiated version of the reference type.
            if (owningType != owningTypeDefinition)
            {
                InstantiatedType boxedType = boxedTypeDefinition.MakeInstantiatedType(owningType.Instantiation);
                MethodDesc thunk = GetMethodForInstantiatedType(thunkDefinition, boxedType);
                //TODO: this might be triggered by a struct that implements an interface with a generic method
                Debug.Assert(!thunk.HasInstantiation);
                return thunk;
            }
            else
            {
                //TODO: this might be triggered by a struct that implements an interface with a generic method
                Debug.Assert(!thunkDefinition.HasInstantiation);
                return thunkDefinition;
            }
        }

        /// <summary>
        /// Returns true of <paramref name="method"/> is a standin method for unboxing thunk target.
        /// </summary>
        public bool IsSpecialUnboxingThunkTargetMethod(MethodDesc method)
        {
            return method.GetTypicalMethodDefinition().GetType() == typeof(ValueTypeInstanceMethodWithHiddenParameter);
        }

        /// <summary>
        /// Returns the real target method of an unboxing stub.
        /// </summary>
        public MethodDesc GetRealSpecialUnboxingThunkTargetMethod(MethodDesc method)
        {
            MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
            MethodDesc methodDefinitionRepresented = ((ValueTypeInstanceMethodWithHiddenParameter)typicalMethod).MethodRepresented;
            return GetMethodForInstantiatedType(methodDefinitionRepresented, (InstantiatedType)method.OwningType);
        }

        private struct BoxedValuetypeHashtableKey
        {
            public readonly MetadataType ValueType;
            public readonly ModuleDesc OwningModule;

            public BoxedValuetypeHashtableKey(MetadataType valueType, ModuleDesc owningModule)
            {
                ValueType = valueType;
                OwningModule = owningModule;
            }
        }

        private class BoxedValuetypeHashtable : LockFreeReaderHashtable<BoxedValuetypeHashtableKey, BoxedValueType>
        {
            protected override int GetKeyHashCode(BoxedValuetypeHashtableKey key)
            {
                return key.ValueType.GetHashCode();
            }
            protected override int GetValueHashCode(BoxedValueType value)
            {
                return value.ValueTypeRepresented.GetHashCode();
            }
            protected override bool CompareKeyToValue(BoxedValuetypeHashtableKey key, BoxedValueType value)
            {
                return Object.ReferenceEquals(key.ValueType, value.ValueTypeRepresented) &&
                    Object.ReferenceEquals(key.OwningModule, value.Module);
            }
            protected override bool CompareValueToValue(BoxedValueType value1, BoxedValueType value2)
            {
                return Object.ReferenceEquals(value1.ValueTypeRepresented, value2.ValueTypeRepresented) &&
                    Object.ReferenceEquals(value1.Module, value2.Module);
            }
            protected override BoxedValueType CreateValueFromKey(BoxedValuetypeHashtableKey key)
            {
                return new BoxedValueType(key.OwningModule, key.ValueType);
            }
        }
        private BoxedValuetypeHashtable _boxedValuetypeHashtable = new BoxedValuetypeHashtable();

        private struct UnboxingThunkHashtableKey
        {
            public readonly MethodDesc TargetMethod;
            public readonly BoxedValueType OwningType;

            public UnboxingThunkHashtableKey(MethodDesc targetMethod, BoxedValueType owningType)
            {
                TargetMethod = targetMethod;
                OwningType = owningType;
            }
        }

        private class UnboxingThunkHashtable : LockFreeReaderHashtable<UnboxingThunkHashtableKey, GenericUnboxingThunk>
        {
            protected override int GetKeyHashCode(UnboxingThunkHashtableKey key)
            {
                return key.TargetMethod.GetHashCode();
            }
            protected override int GetValueHashCode(GenericUnboxingThunk value)
            {
                return value.TargetMethod.GetHashCode();
            }
            protected override bool CompareKeyToValue(UnboxingThunkHashtableKey key, GenericUnboxingThunk value)
            {
                return Object.ReferenceEquals(key.TargetMethod, value.TargetMethod) &&
                    Object.ReferenceEquals(key.OwningType, value.OwningType);
            }
            protected override bool CompareValueToValue(GenericUnboxingThunk value1, GenericUnboxingThunk value2)
            {
                return Object.ReferenceEquals(value1.TargetMethod, value2.TargetMethod) &&
                    Object.ReferenceEquals(value1.OwningType, value2.OwningType);
            }
            protected override GenericUnboxingThunk CreateValueFromKey(UnboxingThunkHashtableKey key)
            {
                return new GenericUnboxingThunk(key.OwningType, key.TargetMethod);
            }
        }
        private UnboxingThunkHashtable _unboxingThunkHashtable = new UnboxingThunkHashtable();

        private class NonGenericUnboxingThunkHashtable : LockFreeReaderHashtable<UnboxingThunkHashtableKey, UnboxingThunk>
        {
            protected override int GetKeyHashCode(UnboxingThunkHashtableKey key)
            {
                return key.TargetMethod.GetHashCode();
            }
            protected override int GetValueHashCode(UnboxingThunk value)
            {
                return value.TargetMethod.GetHashCode();
            }
            protected override bool CompareKeyToValue(UnboxingThunkHashtableKey key, UnboxingThunk value)
            {
                return Object.ReferenceEquals(key.TargetMethod, value.TargetMethod) &&
                    Object.ReferenceEquals(key.OwningType, value.OwningType);
            }
            protected override bool CompareValueToValue(UnboxingThunk value1, UnboxingThunk value2)
            {
                return Object.ReferenceEquals(value1.TargetMethod, value2.TargetMethod) &&
                    Object.ReferenceEquals(value1.OwningType, value2.OwningType);
            }
            protected override UnboxingThunk CreateValueFromKey(UnboxingThunkHashtableKey key)
            {
                return new UnboxingThunk(key.OwningType, key.TargetMethod);
            }
        }

        private NonGenericUnboxingThunkHashtable _nonGenericUnboxingThunkHashtable = new NonGenericUnboxingThunkHashtable();

        /// <summary>
        /// A type with an identical layout to the layout of a boxed value type.
        /// The type has a single field of the type of the valuetype it represents.
        /// </summary>
        private partial class BoxedValueType : MetadataType, INonEmittableType
        {
            private const string BoxedValueFieldName = "BoxedValue";

            public FieldDesc BoxedValue { get; }

            public MetadataType ValueTypeRepresented { get; }

            public override ModuleDesc Module { get; }

            public override string Name => "Boxed_" + ValueTypeRepresented.Name;

            public override string Namespace => ValueTypeRepresented.Namespace;

            public override Instantiation Instantiation => ValueTypeRepresented.Instantiation;
            public override PInvokeStringFormat PInvokeStringFormat => PInvokeStringFormat.AutoClass;
            public override bool IsExplicitLayout => false;
            public override bool IsSequentialLayout => true;
            public override bool IsBeforeFieldInit => false;
            public override MetadataType MetadataBaseType => (MetadataType)Context.GetWellKnownType(WellKnownType.Object);
            public override DefType BaseType => MetadataBaseType;
            public override bool IsSealed => true;
            public override bool IsAbstract => false;
            public override DefType ContainingType => null;
            public override DefType[] ExplicitlyImplementedInterfaces => Array.Empty<DefType>();
            public override TypeSystemContext Context => ValueTypeRepresented.Context;

            public BoxedValueType(ModuleDesc owningModule, MetadataType valuetype)
            {
                // BoxedValueType has the same genericness as the valuetype it's wrapping.
                // Making BoxedValueType wrap the genericness (and be itself nongeneric) would
                // require a crazy name mangling scheme to allow generating stable and unique names
                // for the wrappers.
                Debug.Assert(valuetype.IsTypeDefinition);

                Debug.Assert(valuetype.IsValueType);

                Module = owningModule;
                ValueTypeRepresented = valuetype;

                // Unboxing thunks for byref-like types don't make sense. Byref-like types cannot be boxed.
                // We still allow these to exist in the system, because it's easier than trying to prevent
                // their creation. We create them as if they existed (in lieu of e.g. pointing all of them
                // to the same __unreachable method body) so that the various places that store pointers to
                // them because they want to be able to extract the target instance method can use the same
                // mechanism they use for everything else at runtime.
                // The main difference is that the "Boxed_ValueType" version has no fields. Reference types
                // cannot have byref-like fields.
                if (!valuetype.IsByRefLike)
                    BoxedValue = new BoxedValueField(this);
            }

            public override ClassLayoutMetadata GetClassLayout() => default(ClassLayoutMetadata);
            public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => false;
            public override IEnumerable<MetadataType> GetNestedTypes() => Array.Empty<MetadataType>();
            public override MetadataType GetNestedType(string name) => null;
            protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() => Array.Empty<MethodImplRecord>();
            public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) => Array.Empty<MethodImplRecord>();

            public override int GetHashCode()
            {
                string ns = Namespace;
                var hashCodeBuilder = new Internal.NativeFormat.TypeHashingAlgorithms.HashCodeBuilder(ns);
                if (ns.Length > 0)
                    hashCodeBuilder.Append(".");
                hashCodeBuilder.Append(Name);
                return hashCodeBuilder.ToHashCode();
            }

            protected override TypeFlags ComputeTypeFlags(TypeFlags mask)
            {
                TypeFlags flags = 0;

                if ((mask & TypeFlags.HasGenericVarianceComputed) != 0)
                {
                    flags |= TypeFlags.HasGenericVarianceComputed;
                }

                if ((mask & TypeFlags.CategoryMask) != 0)
                {
                    flags |= TypeFlags.Class;
                }

                flags |= TypeFlags.HasFinalizerComputed;
                flags |= TypeFlags.AttributeCacheComputed;

                return flags;
            }

            public override FieldDesc GetField(string name)
            {
                if (name == BoxedValueFieldName && BoxedValue != null)
                    return BoxedValue;

                return null;
            }

            public override IEnumerable<FieldDesc> GetFields()
            {
                if (BoxedValue != null)
                    return new FieldDesc[] { BoxedValue };

                return Array.Empty<FieldDesc>();
            }

            /// <summary>
            /// Synthetic field on <see cref="BoxedValueType"/>.
            /// </summary>
            private partial class BoxedValueField : FieldDesc
            {
                private BoxedValueType _owningType;

                public override TypeSystemContext Context => _owningType.Context;
                public override TypeDesc FieldType => _owningType.ValueTypeRepresented.InstantiateAsOpen();
                public override bool HasRva => false;
                public override bool IsInitOnly => false;
                public override bool IsLiteral => false;
                public override bool IsStatic => false;
                public override bool IsThreadStatic => false;
                public override DefType OwningType => _owningType;
                public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => false;
                public override string Name => BoxedValueFieldName;

                public BoxedValueField(BoxedValueType owningType)
                {
                    _owningType = owningType;
                }
            }
        }

        /// <summary>
        /// Does a method represent an unboxing stub
        /// </summary>
        public bool IsSpecialUnboxingThunk(MethodDesc method)
        {
            if (method.GetTypicalMethodDefinition().GetType() == typeof(GenericUnboxingThunk))
                return true;

            return false;
        }

        /// <summary>
        /// Convert from an unboxing stub to the actual target method
        /// </summary>
        public MethodDesc GetTargetOfSpecialUnboxingThunk(MethodDesc method)
        {
            MethodDesc typicalMethodTarget = ((GenericUnboxingThunk)method.GetTypicalMethodDefinition()).TargetMethod;

            MethodDesc methodOnInstantiatedType = typicalMethodTarget;
            if (method.OwningType.HasInstantiation)
            {
                InstantiatedType instantiatedType = GetInstantiatedType((MetadataType)typicalMethodTarget.OwningType, method.OwningType.Instantiation);
                methodOnInstantiatedType = GetMethodForInstantiatedType(typicalMethodTarget, instantiatedType);
            }

            MethodDesc instantiatedMethod = methodOnInstantiatedType;
            if (method.HasInstantiation)
            {
                instantiatedMethod = GetInstantiatedMethod(methodOnInstantiatedType, method.Instantiation);
            }

            return instantiatedMethod;
        }

        /// <summary>
        /// Represents a thunk to call shared instance method on boxed valuetypes.
        /// </summary>
        private partial class GenericUnboxingThunk : ILStubMethod
        {
            private MethodDesc _targetMethod;
            private ValueTypeInstanceMethodWithHiddenParameter _nakedTargetMethod;
            private BoxedValueType _owningType;

            public GenericUnboxingThunk(BoxedValueType owningType, MethodDesc targetMethod)
            {
                Debug.Assert(targetMethod.OwningType.IsValueType);
                Debug.Assert(!targetMethod.Signature.IsStatic);
                
                _owningType = owningType;
                _targetMethod = targetMethod;
                _nakedTargetMethod = new ValueTypeInstanceMethodWithHiddenParameter(targetMethod);
            }

            public override TypeSystemContext Context => _targetMethod.Context;

            public override TypeDesc OwningType => _owningType;

            public override MethodSignature Signature => _targetMethod.Signature;

            public MethodDesc TargetMethod => _targetMethod;

            public override string Name
            {
                get
                {
                    return _targetMethod.Name + "_Unbox";
                }
            }

            public override MethodIL EmitIL()
            {
                if (_owningType.BoxedValue == null)
                {
                    // If this is the fake unboxing thunk for ByRef-like types, just make a method that throws.
                    return new ILStubMethodIL(this,
                        new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.throw_ },
                        Array.Empty<LocalVariableDefinition>(),
                        Array.Empty<object>());
                }

                // Generate the unboxing stub. This loosely corresponds to following C#:
                // return BoxedValue.InstanceMethod(this.m_pEEType, [rest of parameters])

                ILEmitter emit = new ILEmitter();
                ILCodeStream codeStream = emit.NewCodeStream();

                FieldDesc eeTypeField = Context.GetWellKnownType(WellKnownType.Object).GetKnownField("m_pEEType");
                FieldDesc boxedValueField = _owningType.BoxedValue.InstantiateAsOpen();

                // Load ByRef to the field with the value of the boxed valuetype
                codeStream.EmitLdArg(0);
                codeStream.Emit(ILOpcode.ldflda, emit.NewToken(boxedValueField));

                // Load the EEType of the boxed valuetype (this is the hidden generic context parameter expected
                // by the (canonical) instance method, but normally not part of the signature in IL).
                codeStream.EmitLdArg(0);
                codeStream.Emit(ILOpcode.ldfld, emit.NewToken(eeTypeField));

                // Load rest of the arguments
                for (int i = 0; i < _targetMethod.Signature.Length; i++)
                {
                    codeStream.EmitLdArg(i + 1);
                }

                // Call an instance method on the target valuetype that has a fake instantiation parameter
                // in it's signature. This will be swapped by the actual instance method after codegen is done.
                codeStream.Emit(ILOpcode.call, emit.NewToken(_nakedTargetMethod.InstantiateAsOpen()));
                codeStream.Emit(ILOpcode.ret);

                return emit.Link(this);
            }
        }

        /// <summary>
        /// Represents a thunk to call instance method on boxed valuetypes.
        /// </summary>
        private partial class UnboxingThunk : ILStubMethod
        {
            private MethodDesc _targetMethod;
            private BoxedValueType _owningType;

            public UnboxingThunk(BoxedValueType owningType, MethodDesc targetMethod)
            {
                Debug.Assert(targetMethod.OwningType.IsValueType);
                Debug.Assert(!targetMethod.Signature.IsStatic);

                _owningType = owningType;
                _targetMethod = targetMethod;
            }

            public override TypeSystemContext Context => _targetMethod.Context;

            public override TypeDesc OwningType => _owningType;

            public override MethodSignature Signature => _targetMethod.Signature;

            public MethodDesc TargetMethod => _targetMethod;

            public override string Name
            {
                get
                {
                    return _targetMethod.Name + "_Unbox";
                }
            }

            public override MethodIL EmitIL()
            {
                if (_owningType.BoxedValue == null)
                {
                    // If this is the fake unboxing thunk for ByRef-like types, just make a method that throws.
                    return new ILStubMethodIL(this,
                        new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.throw_ },
                        Array.Empty<LocalVariableDefinition>(),
                        Array.Empty<object>());
                }

                // Generate the unboxing stub. This loosely corresponds to following C#:
                // return BoxedValue.InstanceMethod([rest of parameters])

                ILEmitter emit = new ILEmitter();
                ILCodeStream codeStream = emit.NewCodeStream();

                FieldDesc boxedValueField = _owningType.BoxedValue.InstantiateAsOpen();

                // unbox to get a pointer to the value type
                codeStream.EmitLdArg(0);
                codeStream.Emit(ILOpcode.ldflda, emit.NewToken(boxedValueField));

                // Load rest of the arguments
                for (int i = 0; i < _targetMethod.Signature.Length; i++)
                {
                    codeStream.EmitLdArg(i + 1);
                }

                // Call an instance method on the target valuetype
                codeStream.Emit(ILOpcode.call, emit.NewToken(_targetMethod.InstantiateAsOpen()));
                codeStream.Emit(ILOpcode.ret);

                return emit.Link(this);
            }
        }

        /// <summary>
        /// Represents an instance method on a generic valuetype with an explicit instantiation parameter in the
        /// signature. This is so that we can refer to the parameter from IL. References to this method will
        /// be replaced by the actual instance method after codegen is done.
        /// </summary>
        internal partial class ValueTypeInstanceMethodWithHiddenParameter : MethodDesc
        {
            private MethodDesc _methodRepresented;
            private MethodSignature _signature;

            public ValueTypeInstanceMethodWithHiddenParameter(MethodDesc methodRepresented)
            {
                Debug.Assert(methodRepresented.OwningType.IsValueType);
                Debug.Assert(!methodRepresented.Signature.IsStatic);
                
                _methodRepresented = methodRepresented;
            }

            public MethodDesc MethodRepresented => _methodRepresented;

            // We really don't want this method to be inlined.
            public override bool IsNoInlining => true;

            public override bool IsInternalCall => true;

            public override bool IsIntrinsic => true;

            public override TypeSystemContext Context => _methodRepresented.Context;
            public override TypeDesc OwningType => _methodRepresented.OwningType;

            public override string Name => _methodRepresented.Name;

            public override MethodSignature Signature
            {
                get
                {
                    if (_signature == null)
                    {
                        TypeDesc[] parameters = new TypeDesc[_methodRepresented.Signature.Length + 1];

                        // Shared instance methods on generic valuetypes have a hidden parameter with the generic context.
                        // We add it to the signature so that we can refer to it from IL.
                        parameters[0] = Context.GetWellKnownType(WellKnownType.Object).GetKnownField("m_pEEType").FieldType;
                        for (int i = 0; i < _methodRepresented.Signature.Length; i++)
                            parameters[i + 1] = _methodRepresented.Signature[i];

                        _signature = new MethodSignature(_methodRepresented.Signature.Flags,
                            _methodRepresented.Signature.GenericParameterCount,
                            _methodRepresented.Signature.ReturnType,
                            parameters);
                    }

                    return _signature;
                }
            }

            public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => false;
        }
    }
}