123 lines · cpp
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-exception6//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 may11// need to be cleaned up to finish non-LTO compilation. llvm.checked.load is12// an example of an instruction that we want to preserve for LTO, but is13// 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 26using namespace llvm;27 28#define DEBUG_TYPE "fatlto-cleanup"29 30namespace {31// Replaces uses of llvm.type.checked.load instructions with unchecked loads.32// In essence, we're undoing the frontends instrumentation, since it isn't33// 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 off38//39// %0 = tail call { ptr, i1 } @llvm.type.checked.load(ptr %vtable, i32 0, metadata !"foo"), !nosanitize !040// %1 = extractvalue { ptr, i1 } %0, 1, !nosanitize !041// br i1 %1, label %cont2, label %trap1, !nosanitize !042//43// trap1: ; preds = %entry44// tail call void @llvm.ubsantrap(i8 2) #3, !nosanitize !045// unreachable, !nosanitize !046//47// cont2: ; preds = %entry48// %2 = extractvalue { ptr, i1 } %0, 0, !nosanitize !049// %call = tail call noundef i64 %2(ptr noundef nonnull align 8 dereferenceable(8) %p1) #450//51// clang-format on52//53// In this sequence, the vtable pointer is first loaded and checked against some54// metadata. The result indicates failure, then the program traps. On the55// success path, the pointer is used to make an indirect call to the function56// pointer loaded from the vtable.57//58// Since we won't be able to lower this correctly later in non-LTO builds, we59// need to drop the special load and trap, and emit a normal load of the60// function pointer from the vtable.61//62// This is straight forward, since the checked load can be replaced w/ a load63// of the vtable pointer and a GEP instruction to index into the vtable and get64// the correct method/function pointer. We replace the "check" with a constant65// indicating success, which allows later passes to simplify control flow and66// remove any now dead instructions.67//68// This logic holds for both llvm.type.checked.load and69// llvm.type.checked.load.relative instructions.70static 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} // namespace106 107PreservedAnalyses 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