Tuesday, July 7, 2015

LLVM Compiler - New Features (August 2014)

Here are some notable points from WWDC 2014 "What's new in LLVM" (August, 2014):

  1. 64-Bit iOS Support.  In XCode 6, the "Standard Architectures" build setting now builds for 64 bit architectures by default.    

  2. arm64 is an entirely new architecture.  So the entire application needs to be built for 64 bit support, including all libraries you depend on.  If you use a lot of libraries, you need to work with your vendors to obtain 64 bit versions. 

  3. "Migration Hints" include: Function type checking, Objective-C Bool type, Type size independence.

  4. C Function calls now must have prototypes defined, prior to calling the function. If a call to a C function is made for which no prototype has been defined, the following error results:  "implicit declaration of function 'other_function' is invalid in C99"

  5. New project setting: "Enable Strict Checking of objc_msgSend Calls" = YES is a recommended setting that enforces you to have strict type checking every time objc_msgSend is used.  The proper way to use objc_msgSend is: 

      • typedef void (*send_type)(void *, SEL, int); 
      • send_type func = (send_type)objc_msgSend;
      • func(object, sel_getUid("foo:"), 5);  
    This is done to let the compiler know what the final receiving type is.  (For optimization purposes)

  6. New Objective-C Boolean Type.   Previously, in 32-bit iOS, a bool is a Signed Character.  Now it's a new type:  BOOL ("_Bool").   If boolean variables were having non-boolean values put into them (such as a -1), the result of the calculation with those values will be different between 32-bit and 64-bit architectures.  This is something to watch out for. 

  7. Pointers are now also 64-bit.  So if we have places in the code that cast from a smaller integer type to a new 64-bit pointer, we now get a compiler warning:  "cast to 'void *' from smaller integer type 'int'  for a piece of code like this:   return (void*)i;   (where i is an int).   

  8. The C language typedefs should be used to address this:   
    1. intptr_t, uintptr_t for saving pointers to integers.    
    2. size_t for array indeces.   
    3. ptrdiff_t for pointer differences.   

  9. The long type is 4 bytes on 32-bit iOS, and is 8-bytes in 64-bit iOS. 

  10. Objective-C Modernization Tool:  Scans older legacy code to identify opportunities to use new features that adopt best practices.   Accessible from "Edit --> Refactor --> Modernize...".   Select which targets to modernize.   Then you select what Options to modernise.  It's a good idea to go 1 option at a time, in order to make it more manageable. 
    1. Add attribute annotations: Adds annotations such as: NS_RETURNS_INNER_POINTER 
    2. Atomicity of inferred properties: Scans your classes for getter/setter methods that should be instead encased in properties, and converts them to properties, adding appropriate Interface definition, such as:
    3. - (NSString *)name; 
      - (void)setName:(NSString *)newName; 
      Will be replaced by: 
       @property (nonatomic, copy) NSString *name;
    1. Infer designated initializer methods: Allows you to tell the compiler which is the main designated initializer for the class. This Option scans all your classes, finds and marks your designated initializers for you.  Example:  - (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
    2. Infer instancetype for method result type:   Makes our initializers more strongly typed.  You get increased type safety.  For explanation, visit:  http://nshipster.com/instancetype/
    3. Infer protocol conformance: Scans classes and finds if all required methods of a particular protocol are implemented.  If they are, then it will add the protocol conformance line in the Header file.   More descriptive and maintainable. 
    4. Infer readonly properties:  Scans for properties which are only read, and puts the "readonly" modifier on them to make that more explicit. 
    5. Infer readwrite properties
    6. ObjC literals: Shortens your code, replacing things like: [NSNumber numberWithInteger:42] with @42 
    7. ObjC subscripting
    8. Only modify public headers
    9. Use NS_ENUM/NS_OPTIONS macros:  New style Enum macroes that specify the type and size to the compiler.  For example: 
    10. typedef  NS_ENUM(NSInteger, UYLType) 

       UYLTypeDefault, 
       UYLTypeSmall, 
       UYLTypeLarge 
      };

  11. User-Defined Modules:  modern alternatives to precompiled headers, which are more fragile.  They also give you:  Fast Compilation, Clear Semantics and Swift Interoperability. 
    1. You can only have 1 pre-compiled header at a time. 
    2. Precompiled headers are fragile because they can be included more than once in a single compilation, or DEFINEs interfering with each other, messing up the inner workings of the imported framework.  for example:  #define count 100 can break your imported libraries by messing up their internal logic. 
    3. Modules are "semantic import", not "textual inclusion", therefore not fragile. 
    4. To define a module, go to the "Packaging" grouping in your XCode build settings, set "Defines Module" to Yes. 
    5. To import a module:   @import ModuleName;  (Import into another target)
    6. #import <ModuleName/ModuleName.h>  (The compiler is smart enough to realize if the framework being imported is modular, and perform an implicit import, rather than a textual one.  But it's good to update to use @import instead to remove ambiguity). 
    7. Do not expose non-modular imports, such as  #import "Postgres.h", in your API headers. Only do textual imports in the implementation. 
    8. You can still define a Macro on the Command line or in XCode build settings, such as:  "-DDEBUG=1" if you want to affect frameworks which are modular.  (Instead of doing:  "#define DEBUG 1" in the source the old way.)

  12. PGO (Profile Guided Optimization) 
    1. Without optimization, compiler assumes that all inputs to your code are equally likely.  It can't know the most used code paths in order to profile the code.  This feature allows providing a profile to the compiler in order to optimize the execution based on the most-used scenario.  This can result in up to %20 performance boost while running your app. 
    2. An "Instrumented App" is generated as a profile, and specified in your project settings, to guide the compiler to optimize your app for that specific profile.  Optimizations include: 
      1. "Inline more aggressively for 'hot' functions", 
      2. "Lay out most common common code paths contiguously"
      3. "Better register allocation"
      4. Other
    3. To collect a profile:  Product --> Perform Action --> "Generate Optimization Profile" command.  Important to exercise all of the performance-oriented code.  (If you have a game with 3 levels, make sure to play all the levels).  
    4. Build with PGO:   Project Build Settings:  "Use Optimization Profile": "Yes" for the release configuration. 
    5. You have to update your profile after you changed the code quite a bit, because you gradually lose the optimization level.  "Out of date" profile warnings will pop up over time, as you change your code.  
    6. Can use Performance tests to drive the profiling.  But the performance tests should be realistic to how users use your app. 

  13. Vectorization in LLVM
    1. Loop vectorization is a compiler optimization that accelerates loops by providing vector hints to the compiler.  Temp vector variables are used to process more of the workload in parallel. 
    2. Now Analysis of complicated loops have been improved.  More loops can now be vectorized.  Loop vectorizer works together with PGO to make better decisions. 
    3. Improved ARM64 and X86 code generation:  Vectorizer understands the underlying process architecture. ,  Generate better optimized code sequences. 
    4. Specialization of loop values:   Creates a second vectorized version of your loop in which required variables are assumed to allow continuous memory access.  Vectorization is applied to it as normal, and the optimized version of the loop is used if that variable condition is true.  Otherwise, the regular optimized version of the loop is used. 
    5. New kind of vectorizer:  SLP Vectorizer.  (Superworld Level Parallelism:  Parallelism beyond loops).   Glues multiple scalars in your code into vector instructions.   Processes multiple scalars at the same time instead of sequentially.  (Speeds up numerically complex applications). 
    6. In XCode 6: Loop and SLP vectorizers are enabled by default in Release mode. 

  14. Acceleration of Javascript Code.  
    1. WebKit is the heart of the Safari web browser.  
    2. Interpreter executes javascript. 
    3. Two JITs (just in time compilers) accelerate JavaScript.   
    4. Interpreter --> Fast JIT --> Optimizing JIT  --> LLVM JIT (new)
    5. There are trade-offs between quality of code and time spent in the compiler. 
    6. Now LLVM is added to the chain to optimize functions which are run thousands of times.   (Heavy Games for example ported to JavaScript from C++, such as Quake 3)
    7. Javascript is dynamically typed.  WebKit uses statistical evidence of the type used previously and optimize for that type.   Then checks are added to prevent overflows by making assumptions and verifying them.  If the assumptions are broken, fall back to the regular interpreter.  (Only it can handle extreme cases, when type actually changes) 
    8. "On-stack replacement" migrates the state of the program from LLVM back to WebKit, and continue execution in the interpreter. 
    9. Compiling with LLVM is very beneficial for math-intensive applications. 
    10. OS10 and iOS on both 32bit and 64 bit now use LLVM in safari. 
Learn how to create your own Programming Language here!!

No comments:

Post a Comment

The Mobile Startup: Episode 5: Some thoughts about tech, and work.

Knowing that You're Bad! I think that if you have never thought of yourself as a bad engineer before, then you are probably a Bad e...