|  | ============================== | 
|  | FaultMaps and implicit checks | 
|  | ============================== | 
|  |  | 
|  | .. contents:: | 
|  | :local: | 
|  | :depth: 2 | 
|  |  | 
|  | Motivation | 
|  | ========== | 
|  |  | 
|  | Code generated by managed language runtimes tend to have checks that | 
|  | are required for safety but never fail in practice.  In such cases, it | 
|  | is profitable to make the non-failing case cheaper even if it makes | 
|  | the failing case significantly more expensive.  This asymmetry can be | 
|  | exploited by folding such safety checks into operations that can be | 
|  | made to fault reliably if the check would have failed, and recovering | 
|  | from such a fault by using a signal handler. | 
|  |  | 
|  | For example, Java requires null checks on objects before they are read | 
|  | from or written to.  If the object is ``null`` then a | 
|  | ``NullPointerException`` has to be thrown, interrupting normal | 
|  | execution.  In practice, however, dereferencing a ``null`` pointer is | 
|  | extremely rare in well-behaved Java programs, and typically the null | 
|  | check can be folded into a nearby memory operation that operates on | 
|  | the same memory location. | 
|  |  | 
|  | The Fault Map Section | 
|  | ===================== | 
|  |  | 
|  | Information about implicit checks generated by LLVM are put in a | 
|  | special "fault map" section.  On Darwin this section is named | 
|  | ``__llvm_faultmaps``. | 
|  |  | 
|  | The format of this section is | 
|  |  | 
|  | .. code-block:: none | 
|  |  | 
|  | Header { | 
|  | uint8  : Fault Map Version (current version is 1) | 
|  | uint8  : Reserved (expected to be 0) | 
|  | uint16 : Reserved (expected to be 0) | 
|  | } | 
|  | uint32 : NumFunctions | 
|  | FunctionInfo[NumFunctions] { | 
|  | uint64 : FunctionAddress | 
|  | uint32 : NumFaultingPCs | 
|  | uint32 : Reserved (expected to be 0) | 
|  | FunctionFaultInfo[NumFaultingPCs] { | 
|  | uint32  : FaultKind = FaultMaps::FaultingLoad (only legal value currently) | 
|  | uint32  : FaultingPCOffset | 
|  | uint32  : HandlerPCOffset | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | The ``ImplicitNullChecks`` pass | 
|  | =============================== | 
|  |  | 
|  | The ``ImplicitNullChecks`` pass transforms explicit control flow for | 
|  | checking if a pointer is ``null``, like: | 
|  |  | 
|  | .. code-block:: llvm | 
|  |  | 
|  | %ptr = call i32* @get_ptr() | 
|  | %ptr_is_null = icmp i32* %ptr, null | 
|  | br i1 %ptr_is_null, label %is_null, label %not_null, !make.implicit !0 | 
|  |  | 
|  | not_null: | 
|  | %t = load i32, i32* %ptr | 
|  | br label %do_something_with_t | 
|  |  | 
|  | is_null: | 
|  | call void @HFC() | 
|  | unreachable | 
|  |  | 
|  | !0 = !{} | 
|  |  | 
|  | to control flow implicit in the instruction loading or storing through | 
|  | the pointer being null checked: | 
|  |  | 
|  | .. code-block:: llvm | 
|  |  | 
|  | %ptr = call i32* @get_ptr() | 
|  | %t = load i32, i32* %ptr  ;; handler-pc = label %is_null | 
|  | br label %do_something_with_t | 
|  |  | 
|  | is_null: | 
|  | call void @HFC() | 
|  | unreachable | 
|  |  | 
|  | This transform happens at the ``MachineInstr`` level, not the LLVM IR | 
|  | level (so the above example is only representative, not literal).  The | 
|  | ``ImplicitNullChecks`` pass runs during codegen, if | 
|  | ``-enable-implicit-null-checks`` is passed to ``llc``. | 
|  |  | 
|  | The ``ImplicitNullChecks`` pass adds entries to the | 
|  | ``__llvm_faultmaps`` section described above as needed. | 
|  |  | 
|  | ``make.implicit`` metadata | 
|  | -------------------------- | 
|  |  | 
|  | Making null checks implicit is an aggressive optimization, and it can | 
|  | be a net performance pessimization if too many memory operations end | 
|  | up faulting because of it.  A language runtime typically needs to | 
|  | ensure that only a negligible number of implicit null checks actually | 
|  | fault once the application has reached a steady state.  A standard way | 
|  | of doing this is by healing failed implicit null checks into explicit | 
|  | null checks via code patching or recompilation.  It follows that there | 
|  | are two requirements an explicit null check needs to satisfy for it to | 
|  | be profitable to convert it to an implicit null check: | 
|  |  | 
|  | 1. The case where the pointer is actually null (i.e. the "failing" | 
|  | case) is extremely rare. | 
|  |  | 
|  | 2. The failing path heals the implicit null check into an explicit | 
|  | null check so that the application does not repeatedly page | 
|  | fault. | 
|  |  | 
|  | The frontend is expected to mark branches that satisfy (1) and (2) | 
|  | using a ``!make.implicit`` metadata node (the actual content of the | 
|  | metadata node is ignored).  Only branches that are marked with | 
|  | ``!make.implicit`` metadata are considered as candidates for | 
|  | conversion into implicit null checks. | 
|  |  | 
|  | (Note that while we could deal with (1) using profiling data, dealing | 
|  | with (2) requires some information not present in branch profiles.) |