Index: llvm/trunk/docs/FaultMaps.rst =================================================================== --- llvm/trunk/docs/FaultMaps.rst +++ llvm/trunk/docs/FaultMaps.rst @@ -64,7 +64,7 @@ %ptr = call i32* @get_ptr() %ptr_is_null = icmp i32* %ptr, null - br i1 %ptr_is_null, label %is_null, label %not_null + br i1 %ptr_is_null, label %is_null, label %not_null, !make.implicit !0 not_null: %t = load i32, i32* %ptr @@ -73,6 +73,8 @@ is_null: call void @HFC() unreachable + + !0 = !{} to control flow implicit in the instruction loading or storing through the pointer being null checked: @@ -94,3 +96,32 @@ 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.) Index: llvm/trunk/lib/CodeGen/ImplicitNullChecks.cpp =================================================================== --- llvm/trunk/lib/CodeGen/ImplicitNullChecks.cpp +++ llvm/trunk/lib/CodeGen/ImplicitNullChecks.cpp @@ -124,6 +124,13 @@ MachineBasicBlock &MBB, SmallVectorImpl &NullCheckList) { typedef TargetInstrInfo::MachineBranchPredicate MachineBranchPredicate; + MDNode *BranchMD = + MBB.getBasicBlock() + ? MBB.getBasicBlock()->getTerminator()->getMetadata("make.implicit") + : nullptr; + if (!BranchMD) + return false; + MachineBranchPredicate MBP; if (TII->AnalyzeBranchPredicate(MBB, MBP, true)) Index: llvm/trunk/test/CodeGen/X86/implicit-null-check-negative.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/implicit-null-check-negative.ll +++ llvm/trunk/test/CodeGen/X86/implicit-null-check-negative.ll @@ -10,7 +10,7 @@ %c = icmp eq i32* %x, null ; It isn't legal to move the load from %x from "not_null" to here -- ; the store to %y could be aliasing it. - br i1 %c, label %is_null, label %not_null + br i1 %c, label %is_null, label %not_null, !make.implicit !0 is_null: ret i32 42 @@ -24,7 +24,7 @@ define i32 @imp_null_check_gep_load(i32* %x) { entry: %c = icmp eq i32* %x, null - br i1 %c, label %is_null, label %not_null + br i1 %c, label %is_null, label %not_null, !make.implicit !0 is_null: ret i32 42 @@ -36,3 +36,19 @@ %t = load i32, i32* %x.gep ret i32 %t } + +define i32 @imp_null_check_load_no_md(i32* %x) { +; This is fine, except it is missing the !make.implicit metadata. + entry: + %c = icmp eq i32* %x, null + br i1 %c, label %is_null, label %not_null + + is_null: + ret i32 42 + + not_null: + %t = load i32, i32* %x + ret i32 %t +} + +!0 = !{} Index: llvm/trunk/test/CodeGen/X86/implicit-null-check.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/implicit-null-check.ll +++ llvm/trunk/test/CodeGen/X86/implicit-null-check.ll @@ -21,7 +21,7 @@ entry: %c = icmp eq i32* %x, null - br i1 %c, label %is_null, label %not_null + br i1 %c, label %is_null, label %not_null, !make.implicit !0 is_null: ret i32 42 @@ -42,7 +42,7 @@ entry: %c = icmp eq i32* %x, null - br i1 %c, label %is_null, label %not_null + br i1 %c, label %is_null, label %not_null, !make.implicit !0 is_null: ret i32 42 @@ -65,7 +65,7 @@ entry: %c = icmp eq i32* %x, null - br i1 %c, label %is_null, label %not_null + br i1 %c, label %is_null, label %not_null, !make.implicit !0 is_null: ret i32 42 @@ -76,6 +76,8 @@ ret i32 %p1 } +!0 = !{} + ; CHECK-LABEL: __LLVM_FaultMaps: ; Version: