brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.1 KiB · 2743931 Raw
414 lines · cpp
1//===-- MemoryOpRemark.cpp - Auto-init remark analysis---------------------===//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// Implementation of the analysis for the "auto-init" remark.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/Transforms/Utils/MemoryOpRemark.h"14#include "llvm/ADT/SmallString.h"15#include "llvm/Analysis/OptimizationRemarkEmitter.h"16#include "llvm/Analysis/ValueTracking.h"17#include "llvm/IR/DebugInfo.h"18#include "llvm/IR/Instructions.h"19#include "llvm/IR/IntrinsicInst.h"20#include <optional>21 22using namespace llvm;23using namespace llvm::ore;24 25MemoryOpRemark::~MemoryOpRemark() = default;26 27bool MemoryOpRemark::canHandle(const Instruction *I, const TargetLibraryInfo &TLI) {28  if (isa<StoreInst>(I))29    return true;30 31  if (auto *II = dyn_cast<IntrinsicInst>(I)) {32    switch (II->getIntrinsicID()) {33    case Intrinsic::memcpy_inline:34    case Intrinsic::memcpy:35    case Intrinsic::memmove:36    case Intrinsic::memset:37    case Intrinsic::memcpy_element_unordered_atomic:38    case Intrinsic::memmove_element_unordered_atomic:39    case Intrinsic::memset_element_unordered_atomic:40      return true;41    default:42      return false;43    }44  }45 46  if (auto *CI = dyn_cast<CallInst>(I)) {47    auto *CF = CI->getCalledFunction();48    if (!CF)49      return false;50 51    if (!CF->hasName())52      return false;53 54    LibFunc LF;55    bool KnownLibCall = TLI.getLibFunc(*CF, LF) && TLI.has(LF);56    if (!KnownLibCall)57      return false;58 59    switch (LF) {60    case LibFunc_memcpy_chk:61    case LibFunc_mempcpy_chk:62    case LibFunc_memset_chk:63    case LibFunc_memmove_chk:64    case LibFunc_memcpy:65    case LibFunc_mempcpy:66    case LibFunc_memset:67    case LibFunc_memmove:68    case LibFunc_bzero:69    case LibFunc_bcopy:70      return true;71    default:72      return false;73    }74  }75 76  return false;77}78 79void MemoryOpRemark::visit(const Instruction *I) {80  // For some of them, we can provide more information:81 82  // For stores:83  // * size84  // * volatile / atomic85  if (auto *SI = dyn_cast<StoreInst>(I)) {86    visitStore(*SI);87    return;88  }89 90  // For intrinsics:91  // * user-friendly name92  // * size93  if (auto *II = dyn_cast<IntrinsicInst>(I)) {94    visitIntrinsicCall(*II);95    return;96  }97 98  // For calls:99  // * known/unknown function (e.g. the compiler knows bzero, but it doesn't100  //                                know my_bzero)101  // * memory operation size102  if (auto *CI = dyn_cast<CallInst>(I)) {103    visitCall(*CI);104    return;105  }106 107  visitUnknown(*I);108}109 110std::string MemoryOpRemark::explainSource(StringRef Type) const {111  return (Type + ".").str();112}113 114StringRef MemoryOpRemark::remarkName(RemarkKind RK) const {115  switch (RK) {116  case RK_Store:117    return "MemoryOpStore";118  case RK_Unknown:119    return "MemoryOpUnknown";120  case RK_IntrinsicCall:121    return "MemoryOpIntrinsicCall";122  case RK_Call:123    return "MemoryOpCall";124  }125  llvm_unreachable("missing RemarkKind case");126}127 128static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline, bool Volatile,129                                                bool Atomic,130                                                DiagnosticInfoIROptimization &R) {131  if (Inline && *Inline)132    R << " Inlined: " << NV("StoreInlined", true) << ".";133  if (Volatile)134    R << " Volatile: " << NV("StoreVolatile", true) << ".";135  if (Atomic)136    R << " Atomic: " << NV("StoreAtomic", true) << ".";137  // Emit the false cases under ExtraArgs. This won't show them in the remark138  // message but will end up in the serialized remarks.139  if ((Inline && !*Inline) || !Volatile || !Atomic)140    R << setExtraArgs();141  if (Inline && !*Inline)142    R << " Inlined: " << NV("StoreInlined", false) << ".";143  if (!Volatile)144    R << " Volatile: " << NV("StoreVolatile", false) << ".";145  if (!Atomic)146    R << " Atomic: " << NV("StoreAtomic", false) << ".";147}148 149static std::optional<uint64_t>150getSizeInBytes(std::optional<uint64_t> SizeInBits) {151  if (!SizeInBits || *SizeInBits % 8 != 0)152    return std::nullopt;153  return *SizeInBits / 8;154}155 156template<typename ...Ts>157std::unique_ptr<DiagnosticInfoIROptimization>158MemoryOpRemark::makeRemark(Ts... Args) {159  switch (diagnosticKind()) {160  case DK_OptimizationRemarkAnalysis:161    return std::make_unique<OptimizationRemarkAnalysis>(Args...);162  case DK_OptimizationRemarkMissed:163    return std::make_unique<OptimizationRemarkMissed>(Args...);164  default:165    llvm_unreachable("unexpected DiagnosticKind");166  }167}168 169void MemoryOpRemark::visitStore(const StoreInst &SI) {170  bool Volatile = SI.isVolatile();171  bool Atomic = SI.isAtomic();172  int64_t Size = DL.getTypeStoreSize(SI.getOperand(0)->getType());173 174  auto R = makeRemark(RemarkPass.data(), remarkName(RK_Store), &SI);175  *R << explainSource("Store") << "\nStore size: " << NV("StoreSize", Size)176     << " bytes.";177  visitPtr(SI.getOperand(1), /*IsRead=*/false, *R);178  inlineVolatileOrAtomicWithExtraArgs(nullptr, Volatile, Atomic, *R);179  ORE.emit(*R);180}181 182void MemoryOpRemark::visitUnknown(const Instruction &I) {183  auto R = makeRemark(RemarkPass.data(), remarkName(RK_Unknown), &I);184  *R << explainSource("Initialization");185  ORE.emit(*R);186}187 188void MemoryOpRemark::visitIntrinsicCall(const IntrinsicInst &II) {189  SmallString<32> CallTo;190  bool Atomic = false;191  bool Inline = false;192  switch (II.getIntrinsicID()) {193  case Intrinsic::memcpy_inline:194    CallTo = "memcpy";195    Inline = true;196    break;197  case Intrinsic::memcpy:198    CallTo = "memcpy";199    break;200  case Intrinsic::memmove:201    CallTo = "memmove";202    break;203  case Intrinsic::memset:204    CallTo = "memset";205    break;206  case Intrinsic::memcpy_element_unordered_atomic:207    CallTo = "memcpy";208    Atomic = true;209    break;210  case Intrinsic::memmove_element_unordered_atomic:211    CallTo = "memmove";212    Atomic = true;213    break;214  case Intrinsic::memset_element_unordered_atomic:215    CallTo = "memset";216    Atomic = true;217    break;218  default:219    return visitUnknown(II);220  }221 222  auto R = makeRemark(RemarkPass.data(), remarkName(RK_IntrinsicCall), &II);223  visitCallee(CallTo.str(), /*KnownLibCall=*/true, *R);224  visitSizeOperand(II.getOperand(2), *R);225 226  auto *CIVolatile = dyn_cast<ConstantInt>(II.getOperand(3));227  // No such thing as a memory intrinsic that is both atomic and volatile.228  bool Volatile = !Atomic && CIVolatile && CIVolatile->getZExtValue();229  switch (II.getIntrinsicID()) {230  case Intrinsic::memcpy_inline:231  case Intrinsic::memcpy:232  case Intrinsic::memmove:233  case Intrinsic::memcpy_element_unordered_atomic:234    visitPtr(II.getOperand(1), /*IsRead=*/true, *R);235    visitPtr(II.getOperand(0), /*IsRead=*/false, *R);236    break;237  case Intrinsic::memset:238  case Intrinsic::memset_element_unordered_atomic:239    visitPtr(II.getOperand(0), /*IsRead=*/false, *R);240    break;241  }242  inlineVolatileOrAtomicWithExtraArgs(&Inline, Volatile, Atomic, *R);243  ORE.emit(*R);244}245 246void MemoryOpRemark::visitCall(const CallInst &CI) {247  Function *F = CI.getCalledFunction();248  if (!F)249    return visitUnknown(CI);250 251  LibFunc LF;252  bool KnownLibCall = TLI.getLibFunc(*F, LF) && TLI.has(LF);253  auto R = makeRemark(RemarkPass.data(), remarkName(RK_Call), &CI);254  visitCallee(F, KnownLibCall, *R);255  visitKnownLibCall(CI, LF, *R);256  ORE.emit(*R);257}258 259template <typename FTy>260void MemoryOpRemark::visitCallee(FTy F, bool KnownLibCall,261                                 DiagnosticInfoIROptimization &R) {262  R << "Call to ";263  if (!KnownLibCall)264    R << NV("UnknownLibCall", "unknown") << " function ";265  R << NV("Callee", F) << explainSource("");266}267 268void MemoryOpRemark::visitKnownLibCall(const CallInst &CI, LibFunc LF,269                                       DiagnosticInfoIROptimization &R) {270  switch (LF) {271  default:272    return;273  case LibFunc_memset_chk:274  case LibFunc_memset:275    visitSizeOperand(CI.getOperand(2), R);276    visitPtr(CI.getOperand(0), /*IsRead=*/false, R);277    break;278  case LibFunc_bzero:279    visitSizeOperand(CI.getOperand(1), R);280    visitPtr(CI.getOperand(0), /*IsRead=*/false, R);281    break;282  case LibFunc_memcpy_chk:283  case LibFunc_mempcpy_chk:284  case LibFunc_memmove_chk:285  case LibFunc_memcpy:286  case LibFunc_mempcpy:287  case LibFunc_memmove:288  case LibFunc_bcopy:289    visitSizeOperand(CI.getOperand(2), R);290    visitPtr(CI.getOperand(1), /*IsRead=*/true, R);291    visitPtr(CI.getOperand(0), /*IsRead=*/false, R);292    break;293  }294}295 296void MemoryOpRemark::visitSizeOperand(Value *V, DiagnosticInfoIROptimization &R) {297  if (auto *Len = dyn_cast<ConstantInt>(V)) {298    uint64_t Size = Len->getZExtValue();299    R << " Memory operation size: " << NV("StoreSize", Size) << " bytes.";300  }301}302 303static std::optional<StringRef> nameOrNone(const Value *V) {304  if (V->hasName())305    return V->getName();306  return std::nullopt;307}308 309void MemoryOpRemark::visitVariable(const Value *V,310                                   SmallVectorImpl<VariableInfo> &Result) {311  if (auto *GV = dyn_cast<GlobalVariable>(V)) {312    auto *Ty = GV->getValueType();313    uint64_t Size = DL.getTypeSizeInBits(Ty).getFixedValue();314    VariableInfo Var{nameOrNone(GV), Size};315    if (!Var.isEmpty())316      Result.push_back(std::move(Var));317    return;318  }319 320  // If we find some information in the debug info, take that.321  bool FoundDI = false;322  // Try to get a dbg.declare, which has a DILocalVariable giving us the323  // real debug info name and size of the variable.324  auto FindDI = [&](const DbgVariableRecord *DVI) {325    if (DILocalVariable *DILV = DVI->getVariable()) {326      std::optional<uint64_t> DISize = getSizeInBytes(DILV->getSizeInBits());327      VariableInfo Var{DILV->getName(), DISize};328      if (!Var.isEmpty()) {329        Result.push_back(std::move(Var));330        FoundDI = true;331      }332    }333  };334  for_each(findDVRDeclares(const_cast<Value *>(V)), FindDI);335 336  if (FoundDI) {337    assert(!Result.empty());338    return;339  }340 341  const auto *AI = dyn_cast<AllocaInst>(V);342  if (!AI)343    return;344 345  // If not, get it from the alloca.346  std::optional<TypeSize> TySize = AI->getAllocationSize(DL);347  std::optional<uint64_t> Size =348      TySize ? std::optional(TySize->getFixedValue()) : std::nullopt;349  VariableInfo Var{nameOrNone(AI), Size};350  if (!Var.isEmpty())351    Result.push_back(std::move(Var));352}353 354void MemoryOpRemark::visitPtr(Value *Ptr, bool IsRead, DiagnosticInfoIROptimization &R) {355  // Find if Ptr is a known variable we can give more information on.356  SmallVector<Value *, 2> Objects;357  getUnderlyingObjectsForCodeGen(Ptr, Objects);358  SmallVector<VariableInfo, 2> VIs;359  for (const Value *V : Objects)360    visitVariable(V, VIs);361 362  if (VIs.empty()) {363    bool CanBeNull;364    bool CanBeFreed;365    uint64_t Size = Ptr->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed);366    if (!Size)367      return;368    VIs.push_back({std::nullopt, Size});369  }370 371  R << (IsRead ? "\n Read Variables: " : "\n Written Variables: ");372  for (unsigned i = 0; i < VIs.size(); ++i) {373    const VariableInfo &VI = VIs[i];374    assert(!VI.isEmpty() && "No extra content to display.");375    if (i != 0)376      R << ", ";377    if (VI.Name)378      R << NV(IsRead ? "RVarName" : "WVarName", *VI.Name);379    else380      R << NV(IsRead ? "RVarName" : "WVarName", "<unknown>");381    if (VI.Size)382      R << " (" << NV(IsRead ? "RVarSize" : "WVarSize", *VI.Size) << " bytes)";383  }384  R << ".";385}386 387bool AutoInitRemark::canHandle(const Instruction *I) {388  if (!I->hasMetadata(LLVMContext::MD_annotation))389    return false;390  return any_of(I->getMetadata(LLVMContext::MD_annotation)->operands(),391                [](const MDOperand &Op) {392                  return isa<MDString>(Op.get()) &&393                         cast<MDString>(Op.get())->getString() == "auto-init";394                });395}396 397std::string AutoInitRemark::explainSource(StringRef Type) const {398  return (Type + " inserted by -ftrivial-auto-var-init.").str();399}400 401StringRef AutoInitRemark::remarkName(RemarkKind RK) const {402  switch (RK) {403  case RK_Store:404    return "AutoInitStore";405  case RK_Unknown:406    return "AutoInitUnknownInstruction";407  case RK_IntrinsicCall:408    return "AutoInitIntrinsicCall";409  case RK_Call:410    return "AutoInitCall";411  }412  llvm_unreachable("missing RemarkKind case");413}414