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. 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 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