123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- //===-- MemoryOpRemark.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/MemoryOpRemark.h"
- #include "llvm/Analysis/OptimizationRemarkEmitter.h"
- #include "llvm/Analysis/ValueTracking.h"
- #include "llvm/IR/DebugInfo.h"
- #include "llvm/IR/Instructions.h"
- #include "llvm/IR/IntrinsicInst.h"
- #include <optional>
- using namespace llvm;
- using namespace llvm::ore;
- MemoryOpRemark::~MemoryOpRemark() = default;
- bool MemoryOpRemark::canHandle(const Instruction *I, const TargetLibraryInfo &TLI) {
- if (isa<StoreInst>(I))
- return true;
- if (auto *II = dyn_cast<IntrinsicInst>(I)) {
- switch (II->getIntrinsicID()) {
- case Intrinsic::memcpy_inline:
- case Intrinsic::memcpy:
- case Intrinsic::memmove:
- case Intrinsic::memset:
- case Intrinsic::memcpy_element_unordered_atomic:
- case Intrinsic::memmove_element_unordered_atomic:
- case Intrinsic::memset_element_unordered_atomic:
- return true;
- default:
- return false;
- }
- }
- if (auto *CI = dyn_cast<CallInst>(I)) {
- auto *CF = CI->getCalledFunction();
- if (!CF)
- return false;
- if (!CF->hasName())
- return false;
- LibFunc LF;
- bool KnownLibCall = TLI.getLibFunc(*CF, LF) && TLI.has(LF);
- if (!KnownLibCall)
- return false;
- switch (LF) {
- case LibFunc_memcpy_chk:
- case LibFunc_mempcpy_chk:
- case LibFunc_memset_chk:
- case LibFunc_memmove_chk:
- case LibFunc_memcpy:
- case LibFunc_mempcpy:
- case LibFunc_memset:
- case LibFunc_memmove:
- case LibFunc_bzero:
- case LibFunc_bcopy:
- return true;
- default:
- return false;
- }
- }
- return false;
- }
- void MemoryOpRemark::visit(const Instruction *I) {
- // For some of them, we can provide more information:
- // For stores:
- // * size
- // * volatile / atomic
- if (auto *SI = dyn_cast<StoreInst>(I)) {
- visitStore(*SI);
- return;
- }
- // For intrinsics:
- // * user-friendly name
- // * size
- if (auto *II = dyn_cast<IntrinsicInst>(I)) {
- visitIntrinsicCall(*II);
- return;
- }
- // For calls:
- // * known/unknown function (e.g. the compiler knows bzero, but it doesn't
- // know my_bzero)
- // * memory operation size
- if (auto *CI = dyn_cast<CallInst>(I)) {
- visitCall(*CI);
- return;
- }
- visitUnknown(*I);
- }
- std::string MemoryOpRemark::explainSource(StringRef Type) const {
- return (Type + ".").str();
- }
- StringRef MemoryOpRemark::remarkName(RemarkKind RK) const {
- switch (RK) {
- case RK_Store:
- return "MemoryOpStore";
- case RK_Unknown:
- return "MemoryOpUnknown";
- case RK_IntrinsicCall:
- return "MemoryOpIntrinsicCall";
- case RK_Call:
- return "MemoryOpCall";
- }
- llvm_unreachable("missing RemarkKind case");
- }
- static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline, bool Volatile,
- bool Atomic,
- DiagnosticInfoIROptimization &R) {
- if (Inline && *Inline)
- R << " Inlined: " << NV("StoreInlined", true) << ".";
- if (Volatile)
- R << " Volatile: " << NV("StoreVolatile", true) << ".";
- if (Atomic)
- R << " Atomic: " << NV("StoreAtomic", true) << ".";
- // Emit the false cases under ExtraArgs. This won't show them in the remark
- // message but will end up in the serialized remarks.
- if ((Inline && !*Inline) || !Volatile || !Atomic)
- R << setExtraArgs();
- if (Inline && !*Inline)
- R << " Inlined: " << NV("StoreInlined", false) << ".";
- if (!Volatile)
- R << " Volatile: " << NV("StoreVolatile", false) << ".";
- if (!Atomic)
- R << " Atomic: " << NV("StoreAtomic", false) << ".";
- }
- static std::optional<uint64_t>
- getSizeInBytes(std::optional<uint64_t> SizeInBits) {
- if (!SizeInBits || *SizeInBits % 8 != 0)
- return std::nullopt;
- return *SizeInBits / 8;
- }
- template<typename ...Ts>
- std::unique_ptr<DiagnosticInfoIROptimization>
- MemoryOpRemark::makeRemark(Ts... Args) {
- switch (diagnosticKind()) {
- case DK_OptimizationRemarkAnalysis:
- return std::make_unique<OptimizationRemarkAnalysis>(Args...);
- case DK_OptimizationRemarkMissed:
- return std::make_unique<OptimizationRemarkMissed>(Args...);
- default:
- llvm_unreachable("unexpected DiagnosticKind");
- }
- }
- void MemoryOpRemark::visitStore(const StoreInst &SI) {
- bool Volatile = SI.isVolatile();
- bool Atomic = SI.isAtomic();
- int64_t Size = DL.getTypeStoreSize(SI.getOperand(0)->getType());
- auto R = makeRemark(RemarkPass.data(), remarkName(RK_Store), &SI);
- *R << explainSource("Store") << "\nStore size: " << NV("StoreSize", Size)
- << " bytes.";
- visitPtr(SI.getOperand(1), /*IsRead=*/false, *R);
- inlineVolatileOrAtomicWithExtraArgs(nullptr, Volatile, Atomic, *R);
- ORE.emit(*R);
- }
- void MemoryOpRemark::visitUnknown(const Instruction &I) {
- auto R = makeRemark(RemarkPass.data(), remarkName(RK_Unknown), &I);
- *R << explainSource("Initialization");
- ORE.emit(*R);
- }
- void MemoryOpRemark::visitIntrinsicCall(const IntrinsicInst &II) {
- SmallString<32> CallTo;
- bool Atomic = false;
- bool Inline = false;
- switch (II.getIntrinsicID()) {
- case Intrinsic::memcpy_inline:
- CallTo = "memcpy";
- Inline = true;
- break;
- 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 visitUnknown(II);
- }
- auto R = makeRemark(RemarkPass.data(), remarkName(RK_IntrinsicCall), &II);
- visitCallee(CallTo.str(), /*KnownLibCall=*/true, *R);
- visitSizeOperand(II.getOperand(2), *R);
- auto *CIVolatile = dyn_cast<ConstantInt>(II.getOperand(3));
- // No such thing as a memory intrinsic that is both atomic and volatile.
- bool Volatile = !Atomic && CIVolatile && CIVolatile->getZExtValue();
- switch (II.getIntrinsicID()) {
- case Intrinsic::memcpy_inline:
- case Intrinsic::memcpy:
- case Intrinsic::memmove:
- case Intrinsic::memcpy_element_unordered_atomic:
- visitPtr(II.getOperand(1), /*IsRead=*/true, *R);
- visitPtr(II.getOperand(0), /*IsRead=*/false, *R);
- break;
- case Intrinsic::memset:
- case Intrinsic::memset_element_unordered_atomic:
- visitPtr(II.getOperand(0), /*IsRead=*/false, *R);
- break;
- }
- inlineVolatileOrAtomicWithExtraArgs(&Inline, Volatile, Atomic, *R);
- ORE.emit(*R);
- }
- void MemoryOpRemark::visitCall(const CallInst &CI) {
- Function *F = CI.getCalledFunction();
- if (!F)
- return visitUnknown(CI);
- LibFunc LF;
- bool KnownLibCall = TLI.getLibFunc(*F, LF) && TLI.has(LF);
- auto R = makeRemark(RemarkPass.data(), remarkName(RK_Call), &CI);
- visitCallee(F, KnownLibCall, *R);
- visitKnownLibCall(CI, LF, *R);
- ORE.emit(*R);
- }
- template <typename FTy>
- void MemoryOpRemark::visitCallee(FTy F, bool KnownLibCall,
- DiagnosticInfoIROptimization &R) {
- R << "Call to ";
- if (!KnownLibCall)
- R << NV("UnknownLibCall", "unknown") << " function ";
- R << NV("Callee", F) << explainSource("");
- }
- void MemoryOpRemark::visitKnownLibCall(const CallInst &CI, LibFunc LF,
- DiagnosticInfoIROptimization &R) {
- switch (LF) {
- default:
- return;
- case LibFunc_memset_chk:
- case LibFunc_memset:
- visitSizeOperand(CI.getOperand(2), R);
- visitPtr(CI.getOperand(0), /*IsRead=*/false, R);
- break;
- case LibFunc_bzero:
- visitSizeOperand(CI.getOperand(1), R);
- visitPtr(CI.getOperand(0), /*IsRead=*/false, R);
- break;
- case LibFunc_memcpy_chk:
- case LibFunc_mempcpy_chk:
- case LibFunc_memmove_chk:
- case LibFunc_memcpy:
- case LibFunc_mempcpy:
- case LibFunc_memmove:
- case LibFunc_bcopy:
- visitSizeOperand(CI.getOperand(2), R);
- visitPtr(CI.getOperand(1), /*IsRead=*/true, R);
- visitPtr(CI.getOperand(0), /*IsRead=*/false, R);
- break;
- }
- }
- void MemoryOpRemark::visitSizeOperand(Value *V, DiagnosticInfoIROptimization &R) {
- if (auto *Len = dyn_cast<ConstantInt>(V)) {
- uint64_t Size = Len->getZExtValue();
- R << " Memory operation size: " << NV("StoreSize", Size) << " bytes.";
- }
- }
- static std::optional<StringRef> nameOrNone(const Value *V) {
- if (V->hasName())
- return V->getName();
- return std::nullopt;
- }
- void MemoryOpRemark::visitVariable(const Value *V,
- SmallVectorImpl<VariableInfo> &Result) {
- if (auto *GV = dyn_cast<GlobalVariable>(V)) {
- auto *Ty = GV->getValueType();
- uint64_t Size = DL.getTypeSizeInBits(Ty).getFixedValue();
- VariableInfo Var{nameOrNone(GV), Size};
- if (!Var.isEmpty())
- Result.push_back(std::move(Var));
- return;
- }
- // 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<Value *>(V))) {
- if (DILocalVariable *DILV = DVI->getVariable()) {
- std::optional<uint64_t> 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<AllocaInst>(V);
- if (!AI)
- return;
- // If not, get it from the alloca.
- std::optional<TypeSize> TySize = AI->getAllocationSize(DL);
- std::optional<uint64_t> Size =
- TySize ? std::optional(TySize->getFixedValue()) : std::nullopt;
- VariableInfo Var{nameOrNone(AI), Size};
- if (!Var.isEmpty())
- Result.push_back(std::move(Var));
- }
- void MemoryOpRemark::visitPtr(Value *Ptr, bool IsRead, DiagnosticInfoIROptimization &R) {
- // Find if Ptr is a known variable we can give more information on.
- SmallVector<Value *, 2> Objects;
- getUnderlyingObjectsForCodeGen(Ptr, Objects);
- SmallVector<VariableInfo, 2> VIs;
- for (const Value *V : Objects)
- visitVariable(V, VIs);
- if (VIs.empty()) {
- bool CanBeNull;
- bool CanBeFreed;
- uint64_t Size = Ptr->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed);
- if (!Size)
- return;
- VIs.push_back({std::nullopt, Size});
- }
- R << (IsRead ? "\n Read Variables: " : "\n Written Variables: ");
- 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(IsRead ? "RVarName" : "WVarName", *VI.Name);
- else
- R << NV(IsRead ? "RVarName" : "WVarName", "<unknown>");
- if (VI.Size)
- R << " (" << NV(IsRead ? "RVarSize" : "WVarSize", *VI.Size) << " bytes)";
- }
- R << ".";
- }
- bool AutoInitRemark::canHandle(const Instruction *I) {
- if (!I->hasMetadata(LLVMContext::MD_annotation))
- return false;
- return any_of(I->getMetadata(LLVMContext::MD_annotation)->operands(),
- [](const MDOperand &Op) {
- return cast<MDString>(Op.get())->getString() == "auto-init";
- });
- }
- std::string AutoInitRemark::explainSource(StringRef Type) const {
- return (Type + " inserted by -ftrivial-auto-var-init.").str();
- }
- StringRef AutoInitRemark::remarkName(RemarkKind RK) const {
- switch (RK) {
- case RK_Store:
- return "AutoInitStore";
- case RK_Unknown:
- return "AutoInitUnknownInstruction";
- case RK_IntrinsicCall:
- return "AutoInitIntrinsicCall";
- case RK_Call:
- return "AutoInitCall";
- }
- llvm_unreachable("missing RemarkKind case");
- }
|