Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

IL2CPP code generation options

Discussion in 'Scripting' started by JoshPeterson, Nov 10, 2015.

  1. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    We've added some new code generation options for IL2CPP in 5.2.2p3 and 4.6.9p2.

    When using the IL2CPP scripting backend, it is possible to control how il2cpp.exe generates C++ code. Specifically, C# attributes can be used to enable or disable the following runtime checks:

    Null checks

    If this option is enabled, C++ code generated by IL2CPP will contain null checks and throw managed NullReferenceException exceptions as necessary. If this option is disabled, the null checks will not be emitted into the generated C++ code. For some projects, disabling this option may improve runtime performance. However, any access to null values in the generated code will not be protected, and may lead to incorrect behavior. Usually the game will crash soon after the null value is dereferenced, but we cannot guarantee this. Disable this option with caution. This option is enabled by default. For WebGL, it is enabled only when exception support is enabled.

    Array bounds checks

    If this option is enabled, C++ code generated by IL2CPP will contain array bounds checks and throw managed IndexOutOfRangeException exceptions as necessary. If this option is disabled, the array bounds checks will not be emitted into the generated C++ code. For some projects, disabling this option may improve runtime performance. However, any access to an array with invalid indices in the generated code will not be protected, and may lead to incorrect behavior, including reading from or writing to arbitrary memory locations. In most cases, these memory accesses will occur without any immediate side effects, and may silently corrupt the state of the game. Disable this option with extreme caution. This option is enabled by default. For WebGL, it is enabled only when exception support is enabled.

    Divide by zero checks

    if this option is enabled, C++ code generated by IL2CPP will contain divide by zero checks for integer division and throw managed DivideByZeroException exceptions as necessary. If this option is disabled, the divide by zero checks on integer division will not be emitted into the generated C++ code. For most projects this option should be disabled. Enable it only if divide by zero checks are required, as these checks have a runtime cost. This option is disabled by default.

    The runtime checks can be enabled or disabled in C# code using the Il2CppSetOptions attribute. To use this attribute, find the Il2CppSetOptionsAttribute.cs source file in the IL2CPP directory in the Unity Editor installation (Data\il2cpp on Windows,Contents/Frameworks/il2cpp on OS X). Copy this source file into the Assets folder in your project. Then the attribute can be used like this:

    Code (CSharp):
    1. [Il2CppSetOption(Option.NullChecks, false)]
    2. public static string MethodWithNullChecksDisabled()
    3. {
    4.   var tmp = new object();
    5.   return tmp.ToString();
    6. }
    The Il2CppSetOptions attribute can be applied to types, methods, and properties. The attribute from the most local scope will be used.

    Code (CSharp):
    1. [Il2CppSetOption(Option.NullChecks, false)]
    2. public class TypeWithNullChecksDisabled
    3. {
    4.   public static string AnyMethod()
    5.   {
    6.     // Null checks will be disabled in this method.
    7.     var tmp = new object();
    8.     return tmp.ToString();
    9.   }
    10.  
    11.   [Il2CppSetOption(Option.NullChecks, true)]
    12.   public static string MethodWithNullChecksEnabled()
    13.   {
    14.     // Null checks will be enabled in this method.
    15.     var tmp = new object();
    16.     return tmp.ToString();
    17.   }
    18. }
    19.  
    20. public class SomeType
    21. {
    22.   [Il2CppSetOption(Option.NullChecks, false)]
    23.   public string PropertyWithNullChecksDisabled
    24.   {
    25.     get
    26.     {
    27.       // Null checks will be disabled here.
    28.       var tmp = new object();
    29.       return tmp.ToString();
    30.     }
    31.     set
    32.     {
    33.       // Null checks will be disabled here.
    34.       value.ToString();
    35.      }
    36.   }
    37.  
    38.   public string PropertyWithNullChecksDisabledOnGetterOnly
    39.   {
    40.     [Il2CppSetOption(Option.NullChecks, false)]
    41.     get
    42.     {
    43.       // Null checks will be disabled here.
    44.       var tmp = new object();
    45.       return tmp.ToString();
    46.     }
    47.     set
    48.     {
    49.       // Null checks will be enabled here.
    50.       value.ToString();
    51.     }
    52.   }
    53. }
    We will also expose global settings for these options in the Player Settings section of the editor. Those will likely land in 5.4, but these options in code are available now. Please let me know if you have any questions about these options.
     
    WillYang, Tak, fantastisch_ and 2 others like this.
  2. Gamba

    Gamba

    Joined:
    Feb 8, 2015
    Posts:
    29
    We recently upgraded to 4.7.0f1, and without adding any of these attributes to our code, all unhandled exceptions are crashing our iOS app. If this feature is the cause of our problems, it doesn't seem like it's enabled by default.

    I also receive a compile error trying to add the option. What namespace are these options in? I found: namespace Unity.IL2CPP.CompilerServices in il2CppSetOptionAttributes.cs, but that doesn't work either.

    Any ideas would be great.
     
    Last edited: Jan 14, 2016
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
     
    GameDeveloper1111 likes this.
  4. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @Gamba

    This should not be used by default. Null checks and array bounds checks should be emitted into the generated C++ code. You can see easily check by searching the generated C++ code for "NullCheck".

    It might be better to debug the generated C++ code in Xcode to help track down the cause of the problem. You can find some details about how to do that here:

    http://blogs.unity3d.com/2015/05/20/il2cpp-internals-debugging-tips-for-generated-code/

    You can also see a demonstration about how to do this here:



    As @Baste mentioned, you'll need to copy the Il2CppSetOptionsAttribute.cs file into the Assets directory of your project to use it. But I suspect it would be better to track down the cause of the issue first.
     
  5. Gamba

    Gamba

    Joined:
    Feb 8, 2015
    Posts:
    29
    Thanks, I found the file, and your right, I don't even want to use it.

    I think the point I was trying to make, was that after upgrading to 4.7, our iOS app started crashing on null exceptions and array bounds checks, where before it didn't. But your post says that null and array bounds checks are enabled by default.After reading the blog post, I checked the call stack at the point of the exception and saw no call of NullCheck. Is that proof that il2cpp.exe is not injecting these checks by default at compile time?
     
  6. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @Gamba

    It might be proof of that, I'm not sure. Were you able to search the generated C++ code for the string "NullCheck"? If you see no instances of that string, then il2cpp.exe is definitely not emitting null checks correctly.

    In the specific case you are debugging, what is the type of the exception being thrown? Can you provide a native call stack for it?
     
  7. Gamba

    Gamba

    Joined:
    Feb 8, 2015
    Posts:
    29
    So, after looking through the generated code, I am seeing references to NullCheck, but a coworker noticed that the Script call optimization setting was set to fast, no exceptions. We changed to slow and safe and found that exceptions were no longer causing crashes and just logging as they used to do.

    We recently shipped our game using Unity 4.6.5p4 with the script call optimization on with no issues, so is this a bug that was in an earlier version that was fixed or is it a bug now that the old behavior has changed? Also, how is script call optimiztion setting supposed to work in relation to new c# attribute you discuss in this thread? Does one override the other?

    Thanks for all the help.

    BTW, I feel like a crazy person, arguing that my code should be allowed to be bad. The problem is that most of the offensive code is in 3rd party tools and I guess just went un-noticed in the older version of Unity. We only upgraded because iOS 9 broke our Russian language support and the version of Unity that applied that patch (4.6.8p2), crashes on level load on iOS.
     
  8. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @Gamba

    I suspect that something has changed with the "fast but no exceptions" setting since 4.6.5p4. So that is probably the difference in behavior you are seeing.

    On the Mono scripting backend, the "fast but no exceptions" setting does two things:
    1. Call from native Unity engine code into your script code (i.e. Start, Update, etc) are done with minimal overhead.
    2. Any managed exceptions that escape out of you scripting code unhandled will trigger the AppDomain.UnhandledException event. Unity defines a default handler for this event that logs the information about the exception for the Unity CrashReport API, then exits the application.
    The current version of the IL2CPP scripting backend does not do (1) above, since it doesn't have the overhead that Mono does in this case at all. In newer versions of Unity, it started to do (2) though, as some third-party crash reporting services rely on this behavior. So the change in behavior was a "feature", but in your case that change in behavior was detrimental.

    The "fast but no exceptions" setting applies to all managed exceptions, not just null check and array bounds check exceptions. So the Il2CppSetOptionAttribute will prevent the run time code from throwing null check and array bounds check exceptions in certain cases, but any other managed exceptions will still be subject to the "fast but no exceptions" setting.

    In general, it is only a good idea to use the Il2CppSetOptionAttribute in specific code where you know that neither a NullReferenceException exception nor a IndexOutOfRangeException exception will occur.
     
  9. Gamba

    Gamba

    Joined:
    Feb 8, 2015
    Posts:
    29
    Thanks a lot for the detailed explanation. I will keep this in mind moving forward..
     
  10. WillYang

    WillYang

    Joined:
    Aug 16, 2016
    Posts:
    1
    Didn't see global setting with unity 5.4, is there any way to set these option globaly?
    Thanks!
     
  11. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @WillYang

    Unfortunately no, these options cannot be set globally. We looked at different ways to expose them, along with the costs and benefits of doing so. In the end we decided that it was best to leave these options at the source code level only, as they don't seem to have a large global impact. Often then do matter in specific hotspots in code, but hopefully those cases can be addressed in the source code.
     
    futurlab_xbox likes this.
  12. Sokharev

    Sokharev

    Joined:
    Oct 10, 2013
    Posts:
    7
    My 5 cents. I've modified dlls to setup nullchecks/arraychecks globally + arrayelement checks and have following results for "almost ready to ship" project:
    1. all default checks enabled : 26.8 MB
    2. no nullchecks/arraychecks : 25.3MB( ie -1.5mb) . please note it's per platform (arm 32bit) . so for arm64 we'll have -1.5mb = 3.0 mb in total.
    3. arrayelement check removal subs about 0.1mb = it's almost nothing.
    =
    i think 3.0 mb quite good deal at Ios( where code size very costly).
    may be good to do following:
    1. for "debug"-"testing" version purposes we have this checks and catching bad stuff.
    2. for "release" version remove checks globally.
    I mean may be it's good to "gift this option to developers with caution and default checks enabled" rather than "no we don't think it's good idea".
    P.S. I'am pretty sure game will crash equally the same manner without checks because a lot of c++ game shipped without "array bounds check"(but of course in critical places we're using asserts )
    but again - it's just my 5 cents.
    P.S.S(important but offtopic) - Is it possible to manage "strip engine" options? i mean for 2d games we don't want physx at all , we don't want terrain at all. navmeshes and other stuff. i think we can easily strip off ~3-4mb per platform.
    Thanks.
     
  13. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Thanks for the information, these are some interesting results. I don't think we're ready to enable these options in general yet though. We'll certainly keep it in mind for a future Unity release.

    We don't have a way to do this now. But there is active development work going on in Unity now to provide options like this for code stripping. We don't have an ETA, but watch for it in new releases.
     
  14. AggroBird

    AggroBird

    Joined:
    Oct 10, 2013
    Posts:
    3
    Hi, any update on the "Set Il2Cpp option globally" topic?

    We currently resorted to adding the Il2CppSetOption to every class and struct in our project, and I would rather like to disable null checks through the player options.
     
    futurlab_xbox likes this.
  15. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @AggroBird

    We don't have an ETA for adding support to the player options. It is something we would like to do, but it is not complete yet.

    Do you notice a useful size or runtime performance difference by disabling null checks throughout the project?
     
  16. daver0_0

    daver0_0

    Joined:
    Nov 22, 2013
    Posts:
    1
  17. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    You can find it at <UnityInstallDir>\Editor\Data\il2cpp\Il2CppSetOptionAttribute.cs. Just drop that in your project.
     
  18. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Little off-topic:

    I've been writing some libraries that do not necessarily need to depend on UnityEngine at all, just like the complete core of my framework, but I'd still like to have custom types to make use of the SerializeFieldAttribute so that I could take advantage of the inspector features when I happen to use the library in Unity projects.

    I could expose interfaces and re-implement the types, but I'm afraid I'd just be adding redundant implementations all over the place just to mark fields with this attribute. I'm also not a friend of public fields, so that is not an option either.

    Long story short:
    Is there any chance you'd consider putting small things like these in a "free to use .dll"? Or is there another simple solution to that (which I haven't figured out)?

    EDIT* Does that even make sense? :confused::D
     
    Last edited: Aug 30, 2017
    briank likes this.
  19. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    I don't think there is a better solution, but I'm probably the wrong person to ask. It worth a separate forum thread, as it is a good question.
     
  20. HermesonBF

    HermesonBF

    Joined:
    Jun 21, 2016
    Posts:
    22
    3 years later, and still no option to disable null checks globally.

    @JoshPeterson If the application appears to be working correctly, an option to remove these unnecessary checks would be interesting.
     
    Last edited: Apr 16, 2020
  21. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    Hi. I really hope this isn't too much of a necropost. I'm really sorry if it is.

    Any recommendations for using this attribute in a separate package? I'm worried that if I share this package with other Unity users, and they have the same attribute declared somewhere in their assets, there would be a conflict. If I do any tricks to avoid a conflict, like declaring the attribute as internal, or putting it in another namespace, would it still work?

    Thank you for your time, I really appreciate it.
     
  22. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    The attribute is matched by type name only, so you can make it internal to your assembly and put it into your own namespace without any problems.
     
    oscarAbraham likes this.
  23. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    Thank you!
     
  24. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    Hi, sorry for the necropost. I'm doing some tests in 2020.3 and I'm finding the attribute doesn't work if it's in another namespace. It has to be inside Unity.IL2CPP.CompilerServices. Do you know if something changed or if it's always been this way? I always assumed it worked, but maybe it never did.

    Thanks, Regards.

    EDIT - Update
    So I made another test and it does work when it's declared internal; it still has to be inside the Unity.IL2CPP.CompilerServices namespace, but it seems to work. Although it'd be nice to be able to put it in a custom namespace to avoid confusion, I think making it internal is enough to be able to use it in shared packages and plugins.
     
    Last edited: Apr 28, 2021
    JoshPeterson likes this.