| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- ==============================
- 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.)
|