xref: /freebsd/contrib/llvm-project/llvm/lib/Transforms/IPO/FatLTOCleanup.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- FatLtoCleanup.cpp - clean up IR for the FatLTO pipeline --*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines operations used to clean up IR for the FatLTO pipeline.
10 // Instrumentation that is beneficial for bitcode sections used in LTO may
11 // need to be cleaned up to finish non-LTO compilation. llvm.checked.load is
12 // an example of an instruction that we want to preserve for LTO, but is
13 // incorrect to leave unchanged during the per-TU compilation in FatLTO.
14 //
15 //===----------------------------------------------------------------------===//
16 
17 #include "llvm/Transforms/IPO/FatLTOCleanup.h"
18 #include "llvm/IR/Function.h"
19 #include "llvm/IR/IRBuilder.h"
20 #include "llvm/IR/Intrinsics.h"
21 #include "llvm/IR/Module.h"
22 #include "llvm/IR/PassManager.h"
23 #include "llvm/IR/Use.h"
24 #include "llvm/Support/Debug.h"
25 
26 using namespace llvm;
27 
28 #define DEBUG_TYPE "fatlto-cleanup"
29 
30 namespace {
31 // Replaces uses of llvm.type.checked.load instructions with unchecked loads.
32 // In essence, we're undoing the frontends instrumentation, since it isn't
33 // correct for the non-LTO part of a FatLTO object.
34 //
35 // llvm.type.checked.load instruction sequences always have a particular form:
36 //
37 // clang-format off
38 //
39 //   %0 = tail call { ptr, i1 } @llvm.type.checked.load(ptr %vtable, i32 0, metadata !"foo"), !nosanitize !0
40 //   %1 = extractvalue { ptr, i1 } %0, 1, !nosanitize !0
41 //   br i1 %1, label %cont2, label %trap1, !nosanitize !0
42 //
43 // trap1:                                            ; preds = %entry
44 //   tail call void @llvm.ubsantrap(i8 2) #3, !nosanitize !0
45 //   unreachable, !nosanitize !0
46 //
47 // cont2:                                            ; preds = %entry
48 //   %2 = extractvalue { ptr, i1 } %0, 0, !nosanitize !0
49 //   %call = tail call noundef i64 %2(ptr noundef nonnull align 8 dereferenceable(8) %p1) #4
50 //
51 // clang-format on
52 //
53 // In this sequence, the vtable pointer is first loaded and checked against some
54 // metadata. The result indicates failure, then the program traps. On the
55 // success path, the pointer is used to make an indirect call to the function
56 // pointer loaded from the vtable.
57 //
58 // Since we won't be able to lower this correctly later in non-LTO builds, we
59 // need to drop the special load and trap, and emit a normal load of the
60 // function pointer from the vtable.
61 //
62 // This is straight forward, since the checked load can be replaced w/ a load
63 // of the vtable pointer and a GEP instruction to index into the vtable and get
64 // the correct method/function pointer. We replace the "check" with a constant
65 // indicating success, which allows later passes to simplify control flow and
66 // remove any now dead instructions.
67 //
68 // This logic holds for both llvm.type.checked.load and
69 // llvm.type.checked.load.relative instructions.
cleanUpTypeCheckedLoad(Module & M,Function & CheckedLoadFn,bool IsRelative)70 static bool cleanUpTypeCheckedLoad(Module &M, Function &CheckedLoadFn,
71                                    bool IsRelative) {
72   bool Changed = false;
73   for (User *User : llvm::make_early_inc_range(CheckedLoadFn.users())) {
74     Instruction *I = dyn_cast<Instruction>(User);
75     if (!I)
76       continue;
77     IRBuilder<> IRB(I);
78     Value *Ptr = I->getOperand(0);
79     Value *Offset = I->getOperand(1);
80     Type *PtrTy = I->getType()->getStructElementType(0);
81     ConstantInt *True = ConstantInt::getTrue(M.getContext());
82     Instruction *Load;
83     if (IsRelative) {
84       Load =
85           IRB.CreateIntrinsic(Intrinsic::load_relative, {Offset->getType()},
86                               {Ptr, Offset}, /*FMFSource=*/nullptr, "rel_load");
87     } else {
88       Value *PtrAdd = IRB.CreatePtrAdd(Ptr, Offset);
89       Load = IRB.CreateLoad(PtrTy, PtrAdd, "vfunc");
90     }
91 
92     Value *Replacement = PoisonValue::get(I->getType());
93     Replacement = IRB.CreateInsertValue(Replacement, True, {1});
94     Replacement = IRB.CreateInsertValue(Replacement, Load, {0});
95     I->replaceAllUsesWith(Replacement);
96 
97     LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": erase " << *I << "\n");
98     I->eraseFromParent();
99     Changed = true;
100   }
101   if (Changed)
102     CheckedLoadFn.eraseFromParent();
103   return Changed;
104 }
105 } // namespace
106 
run(Module & M,ModuleAnalysisManager & AM)107 PreservedAnalyses FatLtoCleanup::run(Module &M, ModuleAnalysisManager &AM) {
108   Function *TypeCheckedLoadFn =
109       Intrinsic::getDeclarationIfExists(&M, Intrinsic::type_checked_load);
110   Function *TypeCheckedLoadRelFn = Intrinsic::getDeclarationIfExists(
111       &M, Intrinsic::type_checked_load_relative);
112 
113   bool Changed = false;
114   if (TypeCheckedLoadFn)
115     Changed |= cleanUpTypeCheckedLoad(M, *TypeCheckedLoadFn, false);
116   if (TypeCheckedLoadRelFn)
117     Changed |= cleanUpTypeCheckedLoad(M, *TypeCheckedLoadRelFn, true);
118 
119   if (Changed)
120     return PreservedAnalyses::none();
121   return PreservedAnalyses::all();
122 }
123