Goto!?
This code brings up an interesting point. What are those labels and goto statements doing in there? These constructs are
not necessary in structured programming! However, IL does not have structured programming concepts like loops and if/then
statements. Since it is lower-level, il2cpp.exe follows lower-level concepts in generated code.
For example, let’s look at the for loop in the HelloWorld_Start_m3 method:
IL_00a8:
{
V_2 = 0;
goto IL_00cc;
}
IL_00af:
{
ObjectU5BU5D_t4* L_19 = ((ObjectU5BU5D_t4*)SZArrayNew(ObjectU5BU5D_t4_il2cpp_TypeInfo_var, 1));
int32_t L_20 = V_2;
Object_t * L_21 =
Box(InitializedTypeInfo(&Int32_t5_il2cpp_TypeInfo), &L_20);
NullCheck(L_19);
IL2CPP_ARRAY_BOUNDS_CHECK(L_19, 0);
ArrayElementTypeCheck (L_19, L_21);
*((Object_t **)(Object_t **)SZArrayLdElema(L_19, 0)) = (Object_t *)L_21;
Debug_LogFormat_m7(NULL /*static, unused*/, (String_t*) &_stringLiteral6, L_19, /*hidden argument*/&Debug_LogFormat_m7_MethodInfo);
V_2 = ((int32_t)(V_2+1));
}
IL_00cc:
{
if ((((int32_t)V_2) < ((int32_t)3)))
{
goto IL_00af;
}
}
Here the V_2 variable is the loop index. Is starts off with a value of 0, then is incremented at the bottom of the loop in this line:
V_2 = ((int32_t)(V_2+1));
The ending condition in the loop is then checked here:
if ((((int32_t)V_2) < ((int32_t)3)))
As long as V_2 is less than 3, the goto statement jumps to the IL_00af label, which is the top of the loop body. You might
be able to guess that il2cpp.exe is currently generating C++ code directly from IL, without using an intermediate abstract
syntax tree representation. If you guessed this, you are correct. You may have also noticed in the Runtime checks section above,
some of the generated code looks like this:
float L_1 = (__this->___x_1);
float L_2 = L_1;
Clearly, the L_2 variable is not necessary here. Most C++ compilers can optimize away this additional assignment, but we would
like to avoid emitting it at all. We’re currently researching the possibility of using an AST to better understand the IL code
and generate better C++ code for cases involving local variables and for loops, among others.
Conclusion
We’ve just scratched the surface of the C++ code generated by the IL2CPP scripting backend for a very simple project.
If you haven’t done so already, I encourage you dig into the generated code in your project. As you explore, keep in mind
that the generated C++ code will look different in future versions of Unity, as we are constantly working to improve the build
and runtime performance of the IL2CPP scripting backend.
By converting IL code to C++, we’ve been able to obtain a nice balance between portable and performant code. We can have
many of the nice developer-friendly features of managed code, while still getting the benefits of quality machine code that C++
compiler provides for various platforms.
In future posts, we’ll explore more generated code, including method calls, sharing of method implementations and wrappers for
calls to native libraries. But next time we will debug some of the generated code for an iOS 64-bit build using Xcode.