# Summary
There is an important JIT time optimization of typeof(T). See: ジェネリックな typeof(T) == typeof(...) は JIT 時最適化で消えるかどうか · GitHub
Current IL2CPP does not optimize this topic.
The generated code needs some runtime function calls and it’s inefficient.
If this typeof(T) == typeof(int) → true/false optimization is performed, the size of the executing file will be reduced and performance will get better.
# More Specific
typeof(T) is a JIT-time constant as well as sizeof(T).
Current IL2CPP.exe emits C++ codes of the Generic Type/Methods.
Those C++ codes are shared among reference-types.
The struct-types have specialized C++ codes.
Sample C# Source Code
I will talk about this code.
using System;
using System.Globalization;
using TMPro;
using UnityEngine;
using Unity.Mathematics;
public unsafe class ArrayEmbed : MonoBehaviour
{
[SerializeField] private TMP_Text text = default;
void Start()
{
text.text = C<int>.D().ToString();
text.text += C<uint>.D().ToString();
text.text += C<string>.D().ToString();
text.text += sizeof(Y).ToString();
text.text += E<decimal>.D().ToString();
text.text += E<byte>.D().ToString();
text.text += E<long>.D().ToString();
}
}
public struct Y
{
public int V;
}
public static class C<T>
{
public static int D()
{
if (typeof(T) == typeof(int)) return 1;
else if (typeof(T) == typeof(string)) return -1;
else return typeof(T).GetHashCode();
}
}
public static unsafe class E<T> where T : unmanaged
{
public static int D()
{
if (typeof(T) == typeof(int)) return sizeof(T);
else return sizeof(T);
}
}
The sample code is compiled and its IL representation is SharpLab.
-
ldtoken
type-name or type-parameter
-
call
System.Type System.Type:: GetTypeFromHandle(System.RuntimeTypeHandle)
-
ldtoken
type-name or type-parameter
-
call
System.Type System.Type:: GetTypeFromHandle(System.RuntimeTypeHandle)
-
call `bool System.Type:: op_Equality(System.Type, System.Type)
The above IL sequence is very important pattern.
2 type name is taken and 1 boolean value is put on the stack.
IL2CPP.exe knows those 2 input type names and can determine true or false.
For the .NET JIT, typeof(T) == typeof(int) is a constant.
typeof(T) == typeof(struct-type) is often written in the if-statement.
If it is replaced with constant boolean value, the C++ compiler will delete the never used branch from the execution file.
Current IL2CPP generated code
// System.Int32 C`1<System.Int32>::smile:()
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t C_1_D_m23EAB822E50B3406618716DA00861C16A3E768D7_gshared (const RuntimeMethod* method)
{
static bool s_Il2CppMethodInitialized;
if (!s_Il2CppMethodInitialized)
{
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&String_t_0_0_0_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Type_t_il2cpp_TypeInfo_var);
s_Il2CppMethodInitialized = true;
}
{
// if (typeof(T) == typeof(int)) return 1;
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_0 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_1;
L_1 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_0, /*hidden argument*/NULL);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_2 = { reinterpret_cast<intptr_t> (Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var) };
Type_t * L_3;
L_3 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_2, /*hidden argument*/NULL);
bool L_4;
L_4 = Type_op_Equality_mA438719A1FDF103C7BBBB08AEF564E7FAEEA0046((Type_t *)L_1, (Type_t *)L_3, /*hidden argument*/NULL);
if (!L_4)
{
goto IL_001d;
}
}
{
// if (typeof(T) == typeof(int)) return 1;
return 1;
}
IL_001d:
{
// else if (typeof(T) == typeof(string)) return -1;
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_5 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_6;
L_6 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_5, /*hidden argument*/NULL);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_7 = { reinterpret_cast<intptr_t> (String_t_0_0_0_var) };
Type_t * L_8;
L_8 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_7, /*hidden argument*/NULL);
bool L_9;
L_9 = Type_op_Equality_mA438719A1FDF103C7BBBB08AEF564E7FAEEA0046((Type_t *)L_6, (Type_t *)L_8, /*hidden argument*/NULL);
if (!L_9)
{
goto IL_003a;
}
}
{
// else if (typeof(T) == typeof(string)) return -1;
return (-1);
}
IL_003a:
{
// else return typeof(T).GetHashCode();
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_10 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_11;
L_11 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_10, /*hidden argument*/NULL);
NullCheck((RuntimeObject *)L_11);
int32_t L_12;
L_12 = VirtFuncInvoker0< int32_t >::Invoke(2 /* System.Int32 System.Object::GetHashCode() */, (RuntimeObject *)L_11);
return L_12;
}
}
// System.Int32 C`1<System.Object>::smile:()
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t C_1_D_m60360274BCBAAC8A294C353B2B0184D60E01B690_gshared (const RuntimeMethod* method)
{
static bool s_Il2CppMethodInitialized;
if (!s_Il2CppMethodInitialized)
{
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&String_t_0_0_0_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Type_t_il2cpp_TypeInfo_var);
s_Il2CppMethodInitialized = true;
}
{
// if (typeof(T) == typeof(int)) return 1;
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_0 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_1;
L_1 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_0, /*hidden argument*/NULL);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_2 = { reinterpret_cast<intptr_t> (Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var) };
Type_t * L_3;
L_3 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_2, /*hidden argument*/NULL);
bool L_4;
L_4 = Type_op_Equality_mA438719A1FDF103C7BBBB08AEF564E7FAEEA0046((Type_t *)L_1, (Type_t *)L_3, /*hidden argument*/NULL);
if (!L_4)
{
goto IL_001d;
}
}
{
// if (typeof(T) == typeof(int)) return 1;
return 1;
}
IL_001d:
{
// else if (typeof(T) == typeof(string)) return -1;
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_5 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_6;
L_6 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_5, /*hidden argument*/NULL);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_7 = { reinterpret_cast<intptr_t> (String_t_0_0_0_var) };
Type_t * L_8;
L_8 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_7, /*hidden argument*/NULL);
bool L_9;
L_9 = Type_op_Equality_mA438719A1FDF103C7BBBB08AEF564E7FAEEA0046((Type_t *)L_6, (Type_t *)L_8, /*hidden argument*/NULL);
if (!L_9)
{
goto IL_003a;
}
}
{
// else if (typeof(T) == typeof(string)) return -1;
return (-1);
}
IL_003a:
{
// else return typeof(T).GetHashCode();
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_10 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_11;
L_11 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_10, /*hidden argument*/NULL);
NullCheck((RuntimeObject *)L_11);
int32_t L_12;
L_12 = VirtFuncInvoker0< int32_t >::Invoke(2 /* System.Int32 System.Object::GetHashCode() */, (RuntimeObject *)L_11);
return L_12;
}
}
sizeof(T) is treated as my proposal.
sizeof(T) is replaced with sizeof(uint8_t).
// System.Int32 E`1<System.Byte>::smile:()
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t E_1_D_m634365F34758541C291A04E1051FDEB0544A1658_gshared (const RuntimeMethod* method)
{
static bool s_Il2CppMethodInitialized;
if (!s_Il2CppMethodInitialized)
{
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Type_t_il2cpp_TypeInfo_var);
s_Il2CppMethodInitialized = true;
}
{
// if (typeof(T) == typeof(int)) return sizeof(T);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_0 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_1;
L_1 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_0, /*hidden argument*/NULL);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_2 = { reinterpret_cast<intptr_t> (Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var) };
Type_t * L_3;
L_3 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_2, /*hidden argument*/NULL);
bool L_4;
L_4 = Type_op_Equality_mA438719A1FDF103C7BBBB08AEF564E7FAEEA0046((Type_t *)L_1, (Type_t *)L_3, /*hidden argument*/NULL);
// if (typeof(T) == typeof(int)) return sizeof(T);
uint32_t L_5 = sizeof(uint8_t);
return L_5;
}
}
// System.Int32 E`1<System.Decimal>::smile:()
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t E_1_D_m78DC72B793035E6CC29AEC8E50901E774F5E6454_gshared (const RuntimeMethod* method)
{
static bool s_Il2CppMethodInitialized;
if (!s_Il2CppMethodInitialized)
{
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Type_t_il2cpp_TypeInfo_var);
s_Il2CppMethodInitialized = true;
}
{
// if (typeof(T) == typeof(int)) return sizeof(T);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_0 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_1;
L_1 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_0, /*hidden argument*/NULL);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_2 = { reinterpret_cast<intptr_t> (Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var) };
Type_t * L_3;
L_3 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_2, /*hidden argument*/NULL);
bool L_4;
L_4 = Type_op_Equality_mA438719A1FDF103C7BBBB08AEF564E7FAEEA0046((Type_t *)L_1, (Type_t *)L_3, /*hidden argument*/NULL);
// if (typeof(T) == typeof(int)) return sizeof(T);
uint32_t L_5 = sizeof(Decimal_t2978B229CA86D3B7BA66A0AEEE014E0DE4F940D7 );
return L_5;
}
}
// System.Int32 E`1<System.Int64>::smile:()
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t E_1_D_m6598740E9AE16F8A622607D19DE92BDBCBF23F1F_gshared (const RuntimeMethod* method)
{
static bool s_Il2CppMethodInitialized;
if (!s_Il2CppMethodInitialized)
{
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&Type_t_il2cpp_TypeInfo_var);
s_Il2CppMethodInitialized = true;
}
{
// if (typeof(T) == typeof(int)) return sizeof(T);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_0 = { reinterpret_cast<intptr_t> (IL2CPP_RGCTX_TYPE(InitializedTypeInfo(method->klass)->rgctx_data, 0)) };
IL2CPP_RUNTIME_CLASS_INIT(Type_t_il2cpp_TypeInfo_var);
Type_t * L_1;
L_1 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_0, /*hidden argument*/NULL);
RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 L_2 = { reinterpret_cast<intptr_t> (Int32_tFDE5F8CD43D10453F6A2E0C77FE48C6CC7009046_0_0_0_var) };
Type_t * L_3;
L_3 = Type_GetTypeFromHandle_m8BB57524FF7F9DB1803BC561D2B3A4DBACEB385E((RuntimeTypeHandle_tC33965ADA3E041E0C94AF05E5CB527B56482CEF9 )L_2, /*hidden argument*/NULL);
bool L_4;
L_4 = Type_op_Equality_mA438719A1FDF103C7BBBB08AEF564E7FAEEA0046((Type_t *)L_1, (Type_t *)L_3, /*hidden argument*/NULL);
// if (typeof(T) == typeof(int)) return sizeof(T);
uint32_t L_5 = sizeof(int64_t);
return L_5;
}
}