//===-- AutoInitRemark.cpp - Auto-init remark analysis---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Implementation of the analysis for the "auto-init" remark. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/AutoInitRemark.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/Transforms/Utils/Local.h" using namespace llvm; using namespace llvm::ore; static void volatileOrAtomicWithExtraArgs(bool Volatile, bool Atomic, OptimizationRemarkMissed &R) { if (Volatile) R << " Volatile: " << NV("StoreVolatile", true) << "."; if (Atomic) R << " Atomic: " << NV("StoreAtomic", true) << "."; // Emit StoreVolatile: false and StoreAtomic: false under ExtraArgs. This // won't show them in the remark message but will end up in the serialized // remarks. if (!Volatile || !Atomic) R << setExtraArgs(); if (!Volatile) R << " Volatile: " << NV("StoreVolatile", false) << "."; if (!Atomic) R << " Atomic: " << NV("StoreAtomic", false) << "."; } static Optional getSizeInBytes(Optional SizeInBits) { if (!SizeInBits || *SizeInBits % 8 != 0) return None; return *SizeInBits / 8; } void AutoInitRemark::inspectStore(StoreInst &SI) { bool Volatile = SI.isVolatile(); bool Atomic = SI.isAtomic(); int64_t Size = DL.getTypeStoreSize(SI.getOperand(0)->getType()); OptimizationRemarkMissed R(RemarkPass.data(), "AutoInitStore", &SI); R << "Store inserted by -ftrivial-auto-var-init.\nStore size: " << NV("StoreSize", Size) << " bytes."; inspectDst(SI.getOperand(1), R); volatileOrAtomicWithExtraArgs(Volatile, Atomic, R); ORE.emit(R); } void AutoInitRemark::inspectUnknown(Instruction &I) { ORE.emit(OptimizationRemarkMissed(RemarkPass.data(), "AutoInitUnknownInstruction", &I) << "Initialization inserted by -ftrivial-auto-var-init."); } void AutoInitRemark::inspectIntrinsicCall(IntrinsicInst &II) { SmallString<32> CallTo; bool Atomic = false; switch (II.getIntrinsicID()) { case Intrinsic::memcpy: CallTo = "memcpy"; break; case Intrinsic::memmove: CallTo = "memmove"; break; case Intrinsic::memset: CallTo = "memset"; break; case Intrinsic::memcpy_element_unordered_atomic: CallTo = "memcpy"; Atomic = true; break; case Intrinsic::memmove_element_unordered_atomic: CallTo = "memmove"; Atomic = true; break; case Intrinsic::memset_element_unordered_atomic: CallTo = "memset"; Atomic = true; break; default: return inspectUnknown(II); } OptimizationRemarkMissed R(RemarkPass.data(), "AutoInitIntrinsic", &II); inspectCallee(StringRef(CallTo), /*KnownLibCall=*/true, R); inspectSizeOperand(II.getOperand(2), R); auto *CIVolatile = dyn_cast(II.getOperand(3)); // No such thing as a memory intrinsic that is both atomic and volatile. bool Volatile = !Atomic && CIVolatile && CIVolatile->getZExtValue(); inspectDst(II.getOperand(0), R); volatileOrAtomicWithExtraArgs(Volatile, Atomic, R); ORE.emit(R); } void AutoInitRemark::inspectCall(CallInst &CI) { Function *F = CI.getCalledFunction(); if (!F) return inspectUnknown(CI); LibFunc LF; bool KnownLibCall = TLI.getLibFunc(*F, LF) && TLI.has(LF); OptimizationRemarkMissed R(RemarkPass.data(), "AutoInitCall", &CI); inspectCallee(F, KnownLibCall, R); inspectKnownLibCall(CI, LF, R); ORE.emit(R); } template void AutoInitRemark::inspectCallee(FTy F, bool KnownLibCall, OptimizationRemarkMissed &R) { R << "Call to "; if (!KnownLibCall) R << NV("UnknownLibCall", "unknown") << " function "; R << NV("Callee", F) << " inserted by -ftrivial-auto-var-init."; } void AutoInitRemark::inspectKnownLibCall(CallInst &CI, LibFunc LF, OptimizationRemarkMissed &R) { switch (LF) { default: return; case LibFunc_bzero: inspectSizeOperand(CI.getOperand(1), R); inspectDst(CI.getOperand(0), R); break; } } void AutoInitRemark::inspectSizeOperand(Value *V, OptimizationRemarkMissed &R) { if (auto *Len = dyn_cast(V)) { uint64_t Size = Len->getZExtValue(); R << " Memory operation size: " << NV("StoreSize", Size) << " bytes."; } } void AutoInitRemark::inspectVariable(const Value *V, SmallVectorImpl &Result) { // If we find some information in the debug info, take that. bool FoundDI = false; // Try to get an llvm.dbg.declare, which has a DILocalVariable giving us the // real debug info name and size of the variable. for (const DbgVariableIntrinsic *DVI : FindDbgAddrUses(const_cast(V))) { if (DILocalVariable *DILV = DVI->getVariable()) { Optional DISize = getSizeInBytes(DILV->getSizeInBits()); VariableInfo Var{DILV->getName(), DISize}; if (!Var.isEmpty()) { Result.push_back(std::move(Var)); FoundDI = true; } } } if (FoundDI) { assert(!Result.empty()); return; } const auto *AI = dyn_cast(V); if (!AI) return; // If not, get it from the alloca. Optional Name = AI->hasName() ? Optional(AI->getName()) : Optional(None); Optional TySize = AI->getAllocationSizeInBits(DL); Optional Size = TySize ? getSizeInBytes(TySize->getFixedSize()) : None; VariableInfo Var{Name, Size}; if (!Var.isEmpty()) Result.push_back(std::move(Var)); } void AutoInitRemark::inspectDst(Value *Dst, OptimizationRemarkMissed &R) { // Find if Dst is a known variable we can give more information on. SmallVector Objects; getUnderlyingObjects(Dst, Objects); SmallVector VIs; for (const Value *V : Objects) inspectVariable(V, VIs); if (VIs.empty()) return; R << "\nVariables: "; for (unsigned i = 0; i < VIs.size(); ++i) { const VariableInfo &VI = VIs[i]; assert(!VI.isEmpty() && "No extra content to display."); if (i != 0) R << ", "; if (VI.Name) R << NV("VarName", *VI.Name); else R << NV("VarName", ""); if (VI.Size) R << " (" << NV("VarSize", *VI.Size) << " bytes)"; } R << "."; }