il2cpp_IL2CPP内部:通用共享实现

il2cpp_IL2CPP内部:通用共享实现

il2cpp

这是IL2CPP Internals系列中的第五 。 在上一篇文章中,我们研究了如何在为IL2CPP脚本编写后端生成的C ++代码中调用方法。 在本文中,我们将探讨如何实现它们。 具体来说,我们将尝试更好地理解IL2CPP生成的代码的最重要功能之一-通用共享。 通用共享允许许多通用方法共享一个通用实现。 这导致IL2CPP脚本后端的可执行文件大小显着减小。 (This is the fifth post in the IL2CPP Internals series. In the last post, we looked at how methods are called in the C++ code generated for the IL2CPP scripting backend. In this post, we will explore how they are implemented. Specifically, we will try to better understand one of the most important features of code generated with IL2CPP – generic sharing. Generic sharing allows many generic methods to share one common implementation. This leads to significant decreases in executable size for the IL2CPP scripting backend.)

Note that generic sharing is not a new idea, both Mono and .Net runtimes use generic sharing as well. Initially, IL2CPP did not perform generic sharing. Recent improvements have made it even more robust and beneficial. Since il2cpp.exe generates C++ code, we can see where the method implementations are shared.

请注意,通用共享不是一个新想法,Mono和.Net运行时也都使用通用共享。 最初,IL2CPP不执行通用共享。 最近的改进使其更加强大和有益。 由于il2cpp.exe生成C ++代码,因此我们可以看到共享方法实现的位置。

We will explore how generic method implementations are shared (or not) for reference types and value types.  We will also investigate how generic parameter constraints affect generic sharing.

我们将探讨如何为引用类型和值类型共享(或不共享)通用方法实现。 我们还将研究通用参数约束如何影响通用共享。

Keep in mind that everything discussed in this series are implementation details. The topics and code discussed here are likely to change in the future. We like to expose and discuss details like this when it is possible though!

请记住,本系列中讨论的所有内容都是实现细节。 此处讨论的主题和代码将来可能会更改。 我们希望尽可能地公开和讨论这样的细节!

What is generic sharing?

什么是通用共享?

Imagine you are writing the implementation for the List<T> class in C#. Would that implementation depend on the type T is? Could you use the same implementation of the Add method for List<string> and List<object>? How about List<DateTime>?

想象一下,您正在用C#编写List<T>类的实现。 该实现取决于类型T吗? 您可以对List<string>List<object>使用Add方法的相同实现吗? List<DateTime>怎么样?

In fact, the power of generics is just that these C# implementations can be shared, and the generic class List<T> will work for any T. But what happens when List is translated from C# to something executable, like assembly code (as Mono does) or C++ code (as IL2CPP does)? Can we still share the implementation of the Add method?

实际上,泛型的力量仅仅是可以共享这些C#实现,而泛型类List<T>将可用于任何T 但是,如果将List从C#转换为可执行文件,例如汇编代码(如Mono)或C ++代码(如IL2CPP),会发生什么? 我们是否仍可以共享Add方法的实现?

Yes, we can share it most of the time. As we’ll discover in this post, the ability to share the implementation of a generic method depends almost entirely on the size of that type T. If T is any reference type (like string or object), then it will always be the size of a pointer. If T is a value type (like int or DateTime), its size may vary, and things get a bit more complex. The more method implementations which can be shared, the smaller the resulting executable code is.

是的,我们大多数时间都可以共享。 正如我们将在这篇文章中发现的,共享通用方法的实现的能力几乎完全取决于类型T的大小。 如果T是任何引用类型(例如stringobject ),则它将始终是指针的大小。 如果T是一个值类型(如intDateTime ),则其大小可能会有所不同,并且事情会变得更加复杂。 可以共享的方法实现越多,生成的可执行代码就越小。

Mark Probst, the developer who implemented generic sharing Mono, has an excellent series of posts on how Mono performs generic sharing. We won’t go into that much depth about generic sharing here. Instead, we will see how and when IL2CPP performs generic sharing. Hopefully this information will help you better analyze and understand the executable size of your project.

实现了通用共享Mono的开发人员Mark Probst撰写了一系列精彩的文章 ,介绍了Mono如何执行通用共享。 我们不会在这里深入探讨泛型共享。 相反,我们将看到IL2CPP如何以及何时执行通用共享。 希望这些信息将帮助您更好地分析和理解项目的可执行文件大小。

What is shared by IL2CPP?

IL2CPP有什么共同点?

Currently, IL2CPP shares generic method implementations for a generic type SomeGenericType<T> when T is:

当前,当T SomeGenericType<T> ,IL2CPP共享通用类型SomeGenericType<T>通用方法实现:

  • Any reference type (e.g. string, object, or any user defined class)

    任何引用类型(例如stringobject或任何用户定义的类)

  • Any integer or enum type

    任何整数或枚举类型

IL2CPP does not share generic method implementations when T is a value type because the size of each value type will differ (based on the size of its fields).

T为值类型时,IL2CPP不共享通用方法实现,因为每种值类型的大小将有所不同(基于其字段的大小)。

Practically, this means that adding a new usage of SomeGenericType<T>, where T is a reference type will have a minimal impact on the executable size. However, if T is a value type, the executable size will be impacted. This behavior is the same for both the Mono and IL2CPP scripting backends. If you want to know more, read on, it’s time to dig into some implementation details!

实际上,这意味着添加SomeGenericType<T>的新用法(其中T是引用类型)对可执行文件大小的影响最小。 但是,如果T是值类型,则可执行文件的大小将受到影响。 Mono和IL2CPP脚本后端的行为相同。 如果您想了解更多信息,请继续阅读,是时候深入了解一些实现细节了!

The setup

设置

I’ll be using Unity 5.0.2p1 on Windows, and building for the WebGL platform. I’ve enabled the “Development Player” option in the build settings, and the “Enable Exceptions” option is set to a value of “None”. The script code for this post starts with a driver method to create instances of the generic types we will investigate:

我将在Windows上使用Unity 5.0.2p1,并为WebGL平台构建。 我已经在构建设置中启用了“ Development Player”选项,并且“ Enable Exceptions”选项设置为“ None”。 这篇文章的脚本代码从驱动程序方法开始,以创建我们将研究的泛型类型的实例:

1
2
3
4
5
6
public void DemonstrateGenericSharing() {
var usesAString = new GenericType<string>();
var usesAClass = new GenericType<AnyClass>();
var usesAValueType = new GenericType<DateTime>();
var interfaceConstrainedType = new InterfaceConstrainedGenericType<ExperimentWithInterface>();
}
1
2
3
4
5
6
public void DemonstrateGenericSharing ( ) {
var usesAString = new GenericType < string > ( ) ;
var usesAClass = new GenericType < AnyClass > ( ) ;
var usesAValueType = new GenericType < DateTime > ( ) ;
var interfaceConstrainedType = new InterfaceConstrainedGenericType < ExperimentWithInterface > ( ) ;
}

Next, we define the types used in this method:

接下来,我们定义此方法中使用的类型:

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
class GenericType<T> {
public T UsesGenericParameter(T value) {
return value;
}
public void DoesNotUseGenericParameter() {}
public U UsesDifferentGenericParameter<U>(U value) {
return value;
}
}
class AnyClass {}
interface AnswerFinderInterface {
int ComputeAnswer();
}
class ExperimentWithInterface : AnswerFinderInterface {
public int ComputeAnswer() {
return 42;
}
}
class InterfaceConstrainedGenericType<T> where T : AnswerFinderInterface {
public int FindTheAnswer(T experiment) {
return experiment.ComputeAnswer();
}
}
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
class GenericType < T > {
public T UsesGenericParameter ( T value ) {
return value ;
}
public void DoesNotUseGenericParameter ( ) { }
public U UsesDifferentGenericParameter < U > ( U value ) {
return value ;
}
}
class AnyClass { }
interface AnswerFinderInterface {
int ComputeAnswer ( ) ;
}
class ExperimentWithInterface : AnswerFinderInterface {
public int ComputeAnswer ( ) {
return 42 ;
}
}
class InterfaceConstrainedGenericType < T > where T : AnswerFinderInterface {
public int FindTheAnswer ( T experiment ) {
return experiment . ComputeAnswer ( ) ;
}
}

And all of code is nested in a class named HelloWorld derived from MonoBehaviour.

并且所有代码都嵌套在派生自MonoBehaviour名为HelloWorld的类中。

If you view the command line for il2cpp.exe, note that it does not contain the --enable-generic-sharing option, as described in the first post in this series. However, generic sharing is still occurring. It is no longer optional, and happens in all cases now.

如果您查看il2cpp.exe的命令行,请注意,它不包含--enable-generic-sharing option ,如本系列第一篇文章中所述。 但是,通用共享仍在发生。 它不再是可选的,并且现在在所有情况下都会发生。

Generic sharing for reference types

参考类型的通用共享

We’ll start by looking at the most often occurring generic sharing case: reference types. Since all reference types in managed code derive from System.Object, all reference types in the generated C++ code derive from the Object_t type. All reference types can then be represented in C++ code using the type Object_t* as a placeholder. We’ll see why this is important in a moment.

我们将从研究最常见的通用共享案例开始:引用类型。 由于托管代码中的所有引用类型均源自System.Object ,因此生成的C ++代码中的所有引用类型均源自Object_t类型。 然后,所有引用类型都可以使用Object_t*类型作为占位符以C ++代码表示。 我们稍后将了解为什么这很重要。

Let’s search for the generated version of the DemonstrateGenericSharing method. In my project it is named HelloWorld_DemonstrateGenericSharing_m4. We’re looking for the method definitions for the four methods in the GenericType class. Using Ctags, we can jump to the method declaration for the GenericType<string> constructor, GenericType_1__ctor_m8. Note that this method declaration is actually a #define statement, mapping the method to another method, GenericType_1__ctor_m10447_gshared.

让我们搜索DemonstrateGenericSharing方法的生成版本。 在我的项目中,它名为HelloWorld_DemonstrateGenericSharing_m4 。 我们正在寻找GenericType类中四个方法的方法定义。 使用Ctags ,我们可以跳转到GenericType<string>构造函数GenericType_1__ctor_m8的方法声明。 请注意,此方法声明实际上是#define语句,将方法映射到另一个方法GenericType_1__ctor_m10447_gshared

Let’s jump back, back and then find the method declarations for the GenericType<AnyClass> type. If we jump to the declaration of the constructor, GenericType_1__ctor_m9, we can see that it is also a #define statement, mapped to the same function, GenericType_1__ctor_m10447_gshared!

让我们来回跳,然后找到GenericType<AnyClass>类型的方法声明。 如果我们跳到构造函数GenericType_1__ctor_m9的声明,我们可以看到它也是#define语句,映射到相同的函数GenericType_1__ctor_m10447_gshared

If we jump to the definition of GenericType_1__ctor_m10447_gshared, we can see from the code comment on the method definition that this method corresponds to the managed method name HelloWorld/GenericType`1<System.Object>::.ctor(). This is the constructor for the GenericType<object> type. This type is called the fully shared type, meaning that given a type GenericType<T>, for any T that is a reference type, the implementation of all methods will use this version, where T is object.

如果我们跳到GenericType_1__ctor_m10447_gshared的定义,可以从方法定义的代码注释中看到,该方法对应于托管方法名称HelloWorld/GenericType`1<System.Object>::.ctor() 。 这是GenericType<object>类型的构造函数。 此类型称为完全共享类型,这意味着给定类型GenericType<T> ,对于任何作为引用类型的T ,所有方法的实现都将使用此版本,其中Tobject

Look just below the constructor in the generated code, and you should see the C++ code for the UsesGenericParameter method:

在生成的代码中的构造函数下方查看,您应该看到UsesGenericParameter方法的C ++代码:

1
2
3
4
5
6
7
extern "C" Object_t * GenericType_1_UsesGenericParameter_m10449_gshared (GenericType_1_t2159 * __this, Object_t * ___value, MethodInfo* method)
{
{
Object_t * L_0 = ___value;
return L_0;
}
}
1
2
3
4
5
6
7
extern "C" Object_t * GenericType_1_UsesGenericParameter_m10449_gshared ( GenericType_1_t2159 * __this , Object_t * ___value , MethodInfo* method )
{
{
Object_t * L_0 = ___value ;
return L_0 ;
}
}

In both places where the generic parameter T is used (the return type and the type of the single managed argument), the generated code uses the Object_t* type. Since all reference types can be represented in the generated code by Object_t*, we can call this single method implementation for any T that is a reference type.

在使用通用参数T两个地方(返回类型和单个托管参数的类型),生成的代码都使用Object_t*类型。 由于所有引用类型都可以由Object_t*表示在生成的代码中, Object_t*我们可以为作为引用类型的任何T调用此单一方法实现。

In the second blog post in this series (about generated code), we mentioned that all method definitions are free functions in C++. The il2cpp.exe utility does not generate overridden methods in C# using C++ inheritance. However, il2cpp.exe does use C++ inheritance for types. If we search the generated code for the string “AnyClass_t” we can find the C++ representation of the C# type AnyClass:

在本系列的第二篇博客文章中(关于生成的代码),我们提到了所有方法定义都是C ++中的*函数。 il2cpp.exe实用程序不会使用C ++继承在C#中生成重写的方法。 但是,il2cpp.exe确实对类型使用C ++继承。 如果我们在生成的代码中搜索字符串“ AnyClass_t”,则可以找到C#类型AnyClass的C ++表示形式:

1
2
3
struct  AnyClass_t1  : public Object_t
{
};
1
2
3
struct  AnyClass _ t1  : public Object_t
{
} ;

Since AnyClass_t1 derives from Object_t, we can pass a pointer to AnyClass_t1 as the argument to the GenericType_1_UsesGenericParameter_m10449_gshared function without problems.

由于AnyClass_t1AnyClass_t1派生的, Object_t我们可以Object_t地将指向AnyClass_t1的指针作为GenericType_1_UsesGenericParameter_m10449_gshared函数的参数传递。

What about the return value though? We can’t return a pointer to a base class where a pointer to a derived class is expected, right? Take a look at the declaration of the GenericType<AnyClass>::UsesGenericParameter method:

那返回值呢? 我们不能返回一个指向派生类的指针的基类的指针,对吗? 看一下GenericType<AnyClass>::UsesGenericParameter方法的声明:

1
#define GenericType_1_UsesGenericParameter_m10452(__this, ___value, method) (( AnyClass_t1 * (*) (GenericType_1_t6 *, AnyClass_t1 *, MethodInfo*))GenericType_1_UsesGenericParameter_m10449_gshared)(__this, ___value, method)
1
#define GenericType_1_UsesGenericParameter_m10452(__this, ___value, method) (( AnyClass_t1 * (*) (GenericType_1_t6 *, AnyClass_t1 *, MethodInfo*))GenericType_1_UsesGenericParameter_m10449_gshared)(__this, ___value, method)

The generated code is actually casting the return value (type Object_t*) to the derived type AnyClass_t1*. So here IL2CPP is lying to the C++ compiler to avoid the C++ type system. Since the C# compiler has already enforced that no code in UsesGenericParameter does anything unreasonable with type T, then IL2CPP is safe to lie to the C++ compiler here.

生成的代码实际上将返回值(类型Object_t* )转换为派生类型AnyClass_t1* 。 所以在这里,IL2CPP向C ++编译器撒谎,以避免使用C ++类型的系统。 由于C#编译器已经强制执行UsesGenericParameter中的任何代码都UsesGenericParameterT类型执行任何不合理的操作,因此IL2CPP可以安全地躺在这里的C ++编译器中。

Generic sharing with constraints

有约束的通用共享

Suppose that we want to allow some methods to be called on an object of type T? Won’t the use of Object_t* prevent that, since we don’t have many methods on System.Object? Yes, this is correct. But we first need to express this idea to the C# compiler using generic constraints.

假设我们希望允许在T类型的对象上调用某些方法? 因为我们在System.Object上没有很多方法,所以使用Object_t*不会阻止这种情况吗? 是的,这是正确的。 但是我们首先需要使用通用约束将这种想法表达给C#编译器。

Take a look again in the script code for this post at the type named InterfaceConstrainedGenericType. This generic type uses a where clause to require that it type T be derived from a given interface, AnswerFinderInterface. This allows the ComputeAnswer method to be called. Recall from the previous blog post about method invocation that calls on interface methods require a lookup in a vtable structure. Since the FindTheAnswer method will make a direct function call on the constrained instance of type T, then the C++ code can still use the fully shared method implementation, with the type T represented by Object_t*.

再次查看此帖子的脚本代码中名为InterfaceConstrainedGenericType的类型。 此泛型类型使用where子句,要求其类型T从给定接口AnswerFinderInterface 。 这允许ComputeAnswer方法被调用。 回想一下以前有关方法调用的博客文章,该方法调用接口方法需要在vtable结构中进行查找。 由于FindTheAnswer方法将对类型T的受约束实例进行直接函数调用,因此C ++代码仍可以使用对象类型TObject_t*表示的完全共享方法实现。

If we start at the implementation of the HelloWorld_DemonstrateGenericSharing_m4 function, then jump to the definition of the InterfaceConstrainedGenericType_1__ctor_m11 function, we can see that this method is again a #define, mapping to the InterfaceConstrainedGenericType_1__ctor_m10456_gshared function. If we look just below that function for the implementation of the InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared function, we can see that indeed, this is the fully shared version of the function, taking an Object_t* argument. It calls the InterfaceFuncInvoker0::Invoke function to actually make the call to the managed ComputeAnswer method.

如果我们从HelloWorld_DemonstrateGenericSharing_m4函数的实现开始,然后跳到InterfaceConstrainedGenericType_1__ctor_m11函数的定义,我们可以看到此方法再次是#define ,映射到InterfaceConstrainedGenericType_1__ctor_m10456_gshared函数。 如果我们仅在该函数的下方查看InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared函数的实现,则可以看到,实际上,这是该函数的完全共享版本,带有Object_t*参数。 它调用InterfaceFuncInvoker0::Invoke函数以实际调用托管的ComputeAnswer方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
extern "C" int32_t InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared (InterfaceConstrainedGenericType_1_t2160 * __this, Object_t * ___experiment, MethodInfo* method)
{
static bool s_Il2CppMethodIntialized;
if (!s_Il2CppMethodIntialized)
{
AnswerFinderInterface_t11_il2cpp_TypeInfo_var = il2cpp_codegen_class_from_type(&AnswerFinderInterface_t11_0_0_0);
s_Il2CppMethodIntialized = true;
}
{
int32_t L_0 = (int32_t)InterfaceFuncInvoker0<int32_t>::Invoke(0 /* System.Int32 HelloWorld/AnswerFinderInterface::ComputeAnswer() */, AnswerFinderInterface_t11_il2cpp_TypeInfo_var, (Object_t *)(*(&amp;amp;___experiment)));
return L_0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
extern "C" int32_t InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared ( InterfaceConstrainedGenericType_1_t2160 * __this , Object_t * ___experiment , MethodInfo* method )
{
static bool s_Il2CppMethodIntialized ;
if ( ! s_Il2CppMethodIntialized )
{
AnswerFinderInterface_t11_il2cpp_TypeInfo_var = il2cpp_codegen_class_from_type ( & AnswerFinderInterface_t11_0_0_0 ) ;
s_Il2CppMethodIntialized = true ;
}
{
int32_t L_0 = ( int32_t ) InterfaceFuncInvoker0 < int32_t > :: Invoke ( 0 /* System.Int32 HelloWorld/AnswerFinderInterface::ComputeAnswer() */ , AnswerFinderInterface_t11_il2cpp_TypeInfo_var , ( Object_t * ) ( * ( & amp ; amp ; ___experiment ) ) ) ;
return L_0 ;
}
}

This all hangs together in the generated C++ code code because IL2CPP treats all managed interfaces like System.Object. This is a useful rule of thumb to help understand the code generated by il2cpp.exe in other cases as well.

由于IL2CPP将所有托管接口(如System.Object都视为对待,因此所有这些都挂在生成的C ++代码中。 在其他情况下,这也是有用的经验法则,可帮助您理解il2cpp.exe生成的代码。

Constraints with a base class

具有基类的约束

In addition to interface constraints, C# allows constraints to be a base class. IL2CPP does not treat all base classes like System.Object, so how does generic sharing work for base class constraints?

除了接口约束,C#还允许约束成为基类。 IL2CPP不会像System.Object那样对待所有基类,那么泛型共享如何对基类约束起作用?

Since base classes are always reference types, IL2CPP uses the fully shared version of the generic methods for these types. Any code which needs to use a field or call a method on the constrained type is performs a cast in C++ to the proper type. Again, here we rely on the C# compiler to correctly enforce the generic constraint, and we lie to the C++ compiler about the type.

由于基类始终是引用类型,因此IL2CPP对这些类型使用通用方法的完全共享版本。 任何需要使用字段或在受约束类型上调用方法的代码都将在C ++中执行强制转换为正确的类型。 同样,这里我们依靠C#编译器正确地执行泛型约束,而我们则向C ++编译器说明类型。

Generic sharing with value types

价值类型的通用共享

Let’s jump back now to the HelloWorld_DemonstrateGenericSharing_m4 function and look at the implementation for GenericType<DateTime>. The DateTime type is a value type, so GenericType<DateTime> is not shared. We can jump to the declaration of constructor for this type, GenericType_1__ctor_m10. There we see a #define, as in the other cases, but the #define maps to the GenericType_1__ctor_m10_gshared function, which is specific to the GenericType<DateTime> class, and is not used by any other class.

现在让我们跳回到HelloWorld_DemonstrateGenericSharing_m4函数,并查看GenericType<DateTime>DateTime类型是一个值类型,因此不共享GenericType<DateTime> 。 我们可以跳转到此类型的构造函数声明GenericType_1__ctor_m10 。 与其他情况一样,在这里我们看到#define ,但是#define映射到GenericType_1__ctor_m10_gshared函数,该函数特定于GenericType<DateTime>类,并且未被任何其他类使用。

Thinking about generic sharing conceptually

从概念上考虑泛型共享

The implementation of generic sharing can be difficult to understand and follow. The problem space itself is fraught with pathological cases (e.g. the curiously recurring template pattern). It can help to think about a few concepts:

通用共享的实现可能很难理解和遵循。 问题空间本身充满了病理情况(例如, 反复出现的模板模式 )。 考虑以下几个概念可能会有所帮助:

  • Every method implementation on a generic type is shared

    通用类型的每个方法实现都是共享的
  • Some generic types only share method implementations with themselves (e.g. generic types with a value type generic parameter, GenericType above)

    一些通用类型仅与它们自己共享方法实现(例如,具有值类型通用参数的通用类型, GenericType above )

  • Generic types with a reference type generic parameter are fully shared – they always use the implementation with System.Object for all type parameters.

    具有引用类型泛型参数的泛型类型是完全共享的 –它们始终将System.Object的实现与所有类型参数一起使用。

  • Generic types with two or more type parameters can be partially shared if at least one of those type parameters is a reference type.

    如果两个或多个类型参数中的至少一个是引用类型,则可以部分共享这些类型参数。

The il2cpp.exe utility always generates the fully shared method implementations for any generic type. It generates other method implementations only when they are used.

il2cpp.exe实用程序始终会为任何泛型类型生成完全共享的方法实现。 仅在使用其他方法实现时,才会生成它们。

Sharing of generic methods

通用方法的共享

Just as method implementations on generic types can be shared, so can method implementation for generic methods. In the original script code, notice that the UsesDifferentGenericParameter method uses a different type parameter than the GenericType class. When we looked at the shared method implementations for the GenericType class, we did not see the UsesDifferentGenericParameter method. If I search the generated code for “UsesDifferentGenericParameter” I see that the implementation of this method is in the GenericMethods0.cpp file:

正如可以共享泛型​​类型的方法实现一样,泛型方法的方法实现也可以共享。 在原始脚本代码中,请注意UsesDifferentGenericParameter方法使用与GenericType类不同的类型参数。 当我们查看GenericType类的共享方法实现时,没有看到UsesDifferentGenericParameter方法。 如果我在生成的代码中搜索“ UsesDifferentGenericParameter”,则会看到此方法的实现位于GenericMethods0.cpp文件中:

1
2
3
4
5
6
7
extern "C" Object_t * GenericType_1_UsesDifferentGenericParameter_TisObject_t_m15243_gshared (GenericType_1_t2159 * __this, Object_t * ___value, MethodInfo* method)
{
{
Object_t * L_0 = ___value;
return L_0;
}
}
1
2
3
4
5
6
7
extern "C" Object_t * GenericType_1_UsesDifferentGenericParameter_TisObject_t_m15243_gshared ( GenericType_1_t2159 * __this , Object_t * ___value , MethodInfo* method )
{
{
Object_t * L_0 = ___value ;
return L_0 ;
}
}

Notice that this the fully shared version of the method implementation, accepting the type Object_t*. Although this method is in a generic type, the behavior would be the same for a generic method in a non-generic type as well. Effectively, il2cpp.exe attempts to always generate the least code possible for method implementations involving generic parameters.

注意,这是方法实现的完全共享版本,接受类型Object_t* 。 尽管此方法是泛型类型,但对于非泛型类型的泛型方法,其行为也将相同。 实际上,il2cpp.exe尝试始终生成涉及通用参数的方法实现的最少代码。

Conclusion

结论

Generic sharing has been one of the most important improvements to the IL2CPP scripting backend since its initial release. It allows the generated C++ code to be as small as possible, sharing method implementations where they do not differ in behavior. As we look to continue to decrease binary size, we will work to take advantage of more opportunities to share method implementations.

自从最初发布以来,通用共享一直是IL2CPP脚本后端最重要的改进之一。 它允许所生成的C ++代码尽可能地小,并在行为无差异的情况下共享方法实现。 当我们希望继续减小二进制大小时,我们将努力利用更多机会来共享方法实现。

In the next post, we will explore how p/invoke wrappers are generated, and how types are marshaled from managed to native code. We will be able to see the cost of marshaling various types, and debug problems with marshaling code.

在下一篇文章中,我们将探讨如何生成p / invoke包装器,以及如何将类型从托管代码整理到本机代码。 我们将能够看到封送各种类型的费用,并调试封送代码的问题。

翻译自: https://blogs.unity3d.com/2015/06/16/il2cpp-internals-generic-sharing-implementation/

il2cpp