From 4bf69fb52b3c445ddcef5043c6b292efd14330e0 Mon Sep 17 00:00:00 2001 From: Jon Roelofs Date: Tue, 18 May 2021 16:11:44 -0700 Subject: [PATCH] [Remarks] Add analysis remarks for memset/memcpy/memmove lengths Differential revision: https://reviews.llvm.org/D102452 --- .../llvm/Analysis/OptimizationRemarkEmitter.h | 3 + .../{AutoInitRemark.h => MemoryOpRemark.h} | 70 +++- llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 21 + .../Transforms/Scalar/AnnotationRemarks.cpp | 44 +- llvm/lib/Transforms/Utils/AutoInitRemark.cpp | 210 ---------- llvm/lib/Transforms/Utils/CMakeLists.txt | 2 +- llvm/lib/Transforms/Utils/MemoryOpRemark.cpp | 381 ++++++++++++++++++ llvm/test/CodeGen/AArch64/memsize-remarks.ll | 373 +++++++++++++++++ .../Util/trivial-auto-var-init-call.ll | 72 +++- .../Util/trivial-auto-var-init-store.ll | 18 +- 10 files changed, 905 insertions(+), 289 deletions(-) rename llvm/include/llvm/Transforms/Utils/{AutoInitRemark.h => MemoryOpRemark.h} (51%) delete mode 100644 llvm/lib/Transforms/Utils/AutoInitRemark.cpp create mode 100644 llvm/lib/Transforms/Utils/MemoryOpRemark.cpp create mode 100644 llvm/test/CodeGen/AArch64/memsize-remarks.ll diff --git a/llvm/include/llvm/Analysis/OptimizationRemarkEmitter.h b/llvm/include/llvm/Analysis/OptimizationRemarkEmitter.h index 7b34b9795040..9aef4605c667 100644 --- a/llvm/include/llvm/Analysis/OptimizationRemarkEmitter.h +++ b/llvm/include/llvm/Analysis/OptimizationRemarkEmitter.h @@ -76,6 +76,9 @@ public: if (F->getContext().getLLVMRemarkStreamer() || F->getContext().getDiagHandlerPtr()->isAnyRemarkEnabled()) { auto R = RemarkBuilder(); + static_assert( + std::is_base_of::value, + "the lambda passed to emit() must return a remark"); emit((DiagnosticInfoOptimizationBase &)R); } } diff --git a/llvm/include/llvm/Transforms/Utils/AutoInitRemark.h b/llvm/include/llvm/Transforms/Utils/MemoryOpRemark.h similarity index 51% rename from llvm/include/llvm/Transforms/Utils/AutoInitRemark.h rename to llvm/include/llvm/Transforms/Utils/MemoryOpRemark.h index 61be76f289d4..54ec87c82073 100644 --- a/llvm/include/llvm/Transforms/Utils/AutoInitRemark.h +++ b/llvm/include/llvm/Transforms/Utils/MemoryOpRemark.h @@ -1,4 +1,4 @@ -//===- AutoInitRemark.h - Auto-init remark analysis -*- C++ -------------*-===// +//===- MemoryOpRemark.h - Memory operation remark analysis -*- C++ ------*-===// // // The LLVM Compiler Infrastructure // @@ -7,13 +7,13 @@ // //===----------------------------------------------------------------------===// // -// Provide more information about instructions with a "auto-init" -// !annotation metadata. +// Provide more information about instructions that copy, move, or initialize +// memory, including those with a "auto-init" !annotation metadata. // //===----------------------------------------------------------------------===// -#ifndef LLVM_TRANSFORMS_UTILS_AUTOINITREMARK_H -#define LLVM_TRANSFORMS_UTILS_AUTOINITREMARK_H +#ifndef LLVM_TRANSFORMS_UTILS_MEMORYOPREMARK_H +#define LLVM_TRANSFORMS_UTILS_MEMORYOPREMARK_H #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -22,6 +22,7 @@ namespace llvm { class CallInst; class DataLayout; +class DiagnosticInfoIROptimization; class Instruction; class IntrinsicInst; class Value; @@ -31,36 +32,49 @@ class StoreInst; // FIXME: Once we get to more remarks like this one, we need to re-evaluate how // much of this logic should actually go into the remark emitter. -struct AutoInitRemark { +struct MemoryOpRemark { OptimizationRemarkEmitter &ORE; StringRef RemarkPass; const DataLayout &DL; const TargetLibraryInfo &TLI; - AutoInitRemark(OptimizationRemarkEmitter &ORE, StringRef RemarkPass, + MemoryOpRemark(OptimizationRemarkEmitter &ORE, StringRef RemarkPass, const DataLayout &DL, const TargetLibraryInfo &TLI) : ORE(ORE), RemarkPass(RemarkPass), DL(DL), TLI(TLI) {} - /// Emit a remark using information from the store's destination, size, etc. - void inspectStore(StoreInst &SI); - /// Emit a generic auto-init remark. - void inspectUnknown(Instruction &I); - /// Emit a remark using information from known intrinsic calls. - void inspectIntrinsicCall(IntrinsicInst &II); - /// Emit a remark using information from known function calls. - void inspectCall(CallInst &CI); + virtual ~MemoryOpRemark(); + + /// \return true iff the instruction is understood by MemoryOpRemark. + static bool canHandle(const Instruction *I, const TargetLibraryInfo &TLI); + + void visit(const Instruction *I); + +protected: + virtual std::string explainSource(StringRef Type); + + enum RemarkKind { RK_Store, RK_Unknown, RK_IntrinsicCall, RK_Call }; + virtual StringRef remarkName(RemarkKind RK); private: + /// Emit a remark using information from the store's destination, size, etc. + void visitStore(const StoreInst &SI); + /// Emit a generic auto-init remark. + void visitUnknown(const Instruction &I); + /// Emit a remark using information from known intrinsic calls. + void visitIntrinsicCall(const IntrinsicInst &II); + /// Emit a remark using information from known function calls. + void visitCall(const CallInst &CI); + /// Add callee information to a remark: whether it's known, the function name, /// etc. template - void inspectCallee(FTy F, bool KnownLibCall, OptimizationRemarkMissed &R); + void visitCallee(FTy F, bool KnownLibCall, OptimizationRemarkMissed &R); /// Add operand information to a remark based on knowledge we have for known /// libcalls. - void inspectKnownLibCall(CallInst &CI, LibFunc LF, - OptimizationRemarkMissed &R); + void visitKnownLibCall(const CallInst &CI, LibFunc LF, + OptimizationRemarkMissed &R); /// Add the memory operation size to a remark. - void inspectSizeOperand(Value *V, OptimizationRemarkMissed &R); + void visitSizeOperand(Value *V, OptimizationRemarkMissed &R); struct VariableInfo { Optional Name; @@ -70,8 +84,22 @@ private: /// Gather more information about \p V as a variable. This can be debug info, /// information from the alloca, etc. Since \p V can represent more than a /// single variable, they will all be added to the remark. - void inspectDst(Value *Dst, OptimizationRemarkMissed &R); - void inspectVariable(const Value *V, SmallVectorImpl &Result); + void visitPtr(Value *V, bool IsSrc, OptimizationRemarkMissed &R); + void visitVariable(const Value *V, SmallVectorImpl &Result); +}; + +/// Special case for -ftrivial-auto-var-init remarks. +struct AutoInitRemark : public MemoryOpRemark { + AutoInitRemark(OptimizationRemarkEmitter &ORE, StringRef RemarkPass, + const DataLayout &DL, const TargetLibraryInfo &TLI) + : MemoryOpRemark(ORE, RemarkPass, DL, TLI) {} + + /// \return true iff the instruction is understood by AutoInitRemark. + static bool canHandle(const Instruction *I); + +protected: + virtual std::string explainSource(StringRef Type) override; + virtual StringRef remarkName(RemarkKind RK) override; }; } // namespace llvm diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index be7a7641d5b4..bc9c307957c7 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -72,6 +72,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetIntrinsicInfo.h" #include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/Utils/MemoryOpRemark.h" #include #include #include @@ -97,6 +98,7 @@ INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass) INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(StackProtector) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_END(IRTranslator, DEBUG_TYPE, "IRTranslator LLVM IR -> MI", false, false) @@ -164,6 +166,8 @@ void IRTranslator::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); if (OptLevel != CodeGenOpt::None) AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); getSelectionDAGFallbackAnalysisUsage(AU); MachineFunctionPass::getAnalysisUsage(AU); } @@ -1815,6 +1819,14 @@ bool IRTranslator::translateConstrainedFPIntrinsic( bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID, MachineIRBuilder &MIRBuilder) { + if (auto *MI = dyn_cast(&CI)) { + const Function &F = *MI->getParent()->getParent(); + auto &TLI = getAnalysis().getTLI(F); + if (MemoryOpRemark::canHandle(MI, TLI)) { + MemoryOpRemark R(*ORE, "memsize", *DL, TLI); + R.visit(MI); + } + } // If this is a simple intrinsic (that is, we just need to add a def of // a vreg, and uses for each arg operand, then translate it. @@ -2244,6 +2256,15 @@ bool IRTranslator::translateCallBase(const CallBase &CB, Args.push_back(getOrCreateVRegs(*Arg)); } + if (auto *CI = dyn_cast(&CB)) { + const Function &F = *CI->getParent()->getParent(); + auto &TLI = getAnalysis().getTLI(F); + if (MemoryOpRemark::canHandle(CI, TLI)) { + MemoryOpRemark R(*ORE, "memsize", *DL, TLI); + R.visit(CI); + } + } + // We don't set HasCalls on MFI here yet because call lowering may decide to // optimize into tail calls. Instead, we defer that to selection where a final // scan is done to check if any instructions are calls. diff --git a/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp b/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp index c5bdfb4d8f6a..a5e65ffc45fe 100644 --- a/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp +++ b/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp @@ -22,7 +22,7 @@ #include "llvm/Pass.h" #include "llvm/Support/Debug.h" #include "llvm/Transforms/Scalar.h" -#include "llvm/Transforms/Utils/AutoInitRemark.h" +#include "llvm/Transforms/Utils/MemoryOpRemark.h" using namespace llvm; using namespace llvm::ore; @@ -35,45 +35,13 @@ static void tryEmitAutoInitRemark(ArrayRef Instructions, const TargetLibraryInfo &TLI) { // For every auto-init annotation generate a separate remark. for (Instruction *I : Instructions) { - if (!I->hasMetadata(LLVMContext::MD_annotation)) + if (!AutoInitRemark::canHandle(I)) continue; - for (const MDOperand &Op : - I->getMetadata(LLVMContext::MD_annotation)->operands()) { - if (cast(Op.get())->getString() != "auto-init") - continue; - Function &F = *I->getParent()->getParent(); - const DataLayout &DL = F.getParent()->getDataLayout(); - AutoInitRemark Remark(ORE, REMARK_PASS, DL, TLI); - // For some of them, we can provide more information: - - // For stores: - // * size - // * volatile / atomic - if (auto *SI = dyn_cast(I)) { - Remark.inspectStore(*SI); - continue; - } - - // For intrinsics: - // * user-friendly name - // * size - if (auto *II = dyn_cast(I)) { - Remark.inspectIntrinsicCall(*II); - continue; - } - - // 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(I)) { - Remark.inspectCall(*CI); - continue; - } - - Remark.inspectUnknown(*I); - } + Function &F = *I->getParent()->getParent(); + const DataLayout &DL = F.getParent()->getDataLayout(); + AutoInitRemark Remark(ORE, REMARK_PASS, DL, TLI); + Remark.visit(I); } } diff --git a/llvm/lib/Transforms/Utils/AutoInitRemark.cpp b/llvm/lib/Transforms/Utils/AutoInitRemark.cpp deleted file mode 100644 index 97401a741ac4..000000000000 --- a/llvm/lib/Transforms/Utils/AutoInitRemark.cpp +++ /dev/null @@ -1,210 +0,0 @@ -//===-- 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/DebugInfo.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.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 << "."; -} diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt index 2e100310e6fc..be4f7125eb85 100644 --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -3,7 +3,6 @@ add_llvm_component_library(LLVMTransformUtils AMDGPUEmitPrintf.cpp ASanStackFrameLayout.cpp AssumeBundleBuilder.cpp - AutoInitRemark.cpp BasicBlockUtils.cpp BreakCriticalEdges.cpp BuildLibCalls.cpp @@ -48,6 +47,7 @@ add_llvm_component_library(LLVMTransformUtils LowerMemIntrinsics.cpp LowerSwitch.cpp MatrixUtils.cpp + MemoryOpRemark.cpp Mem2Reg.cpp MetaRenamer.cpp ModuleUtils.cpp diff --git a/llvm/lib/Transforms/Utils/MemoryOpRemark.cpp b/llvm/lib/Transforms/Utils/MemoryOpRemark.cpp new file mode 100644 index 000000000000..3e4ac25ed049 --- /dev/null +++ b/llvm/lib/Transforms/Utils/MemoryOpRemark.cpp @@ -0,0 +1,381 @@ +//===-- 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" + +using namespace llvm; +using namespace llvm::ore; + +MemoryOpRemark::~MemoryOpRemark() = default; + +bool MemoryOpRemark::canHandle(const Instruction *I, const TargetLibraryInfo &TLI) { + if (isa(I)) + return true; + + if (auto *II = dyn_cast(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(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(I)) { + visitStore(*SI); + return; + } + + // For intrinsics: + // * user-friendly name + // * size + if (auto *II = dyn_cast(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(I)) { + visitCall(*CI); + return; + } + + visitUnknown(*I); +} + +std::string MemoryOpRemark::explainSource(StringRef Type) { + return (Type + ".").str(); +} + +StringRef MemoryOpRemark::remarkName(RemarkKind RK) { + switch (RK) { + case RK_Store: + return "MemoryOpStore"; + case RK_Unknown: + return "MemoryOpUnknown"; + case RK_IntrinsicCall: + return "MemoryOpIntrinsicCall"; + case RK_Call: + return "MemoryOpCall"; + } +} + +static void inlineVolatileOrAtomicWithExtraArgs(bool *Inline, bool Volatile, + bool Atomic, + OptimizationRemarkMissed &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 Optional getSizeInBytes(Optional SizeInBits) { + if (!SizeInBits || *SizeInBits % 8 != 0) + return None; + return *SizeInBits / 8; +} + +void MemoryOpRemark::visitStore(const StoreInst &SI) { + bool Volatile = SI.isVolatile(); + bool Atomic = SI.isAtomic(); + int64_t Size = DL.getTypeStoreSize(SI.getOperand(0)->getType()); + + OptimizationRemarkMissed R(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) { + OptimizationRemarkMissed R(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); + } + + OptimizationRemarkMissed R(RemarkPass.data(), remarkName(RK_IntrinsicCall), + &II); + visitCallee(StringRef(CallTo), /*KnownLibCall=*/true, R); + visitSizeOperand(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(); + 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); + OptimizationRemarkMissed R(RemarkPass.data(), remarkName(RK_Call), &CI); + visitCallee(F, KnownLibCall, R); + visitKnownLibCall(CI, LF, R); + ORE.emit(R); +} + +template +void MemoryOpRemark::visitCallee(FTy F, bool KnownLibCall, + OptimizationRemarkMissed &R) { + R << "Call to "; + if (!KnownLibCall) + R << NV("UnknownLibCall", "unknown") << " function "; + R << NV("Callee", F) << explainSource(""); +} + +void MemoryOpRemark::visitKnownLibCall(const CallInst &CI, LibFunc LF, + OptimizationRemarkMissed &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, OptimizationRemarkMissed &R) { + if (auto *Len = dyn_cast(V)) { + uint64_t Size = Len->getZExtValue(); + R << " Memory operation size: " << NV("StoreSize", Size) << " bytes."; + } +} + +void MemoryOpRemark::visitVariable(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 MemoryOpRemark::visitPtr(Value *Ptr, bool IsRead, OptimizationRemarkMissed &R) { + // Find if Ptr is a known variable we can give more information on. + SmallVector Objects; + getUnderlyingObjects(Ptr, Objects); + SmallVector 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({None, Size}); + } + + R << (IsRead ? "\nRead Variables: " : "\nWritten 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", ""); + 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(Op.get())->getString() == "auto-init"; + }); +} + +std::string AutoInitRemark::explainSource(StringRef Type) { + return (Type + " inserted by -ftrivial-auto-var-init.").str(); +} + +StringRef AutoInitRemark::remarkName(RemarkKind RK) { + switch (RK) { + case RK_Store: + return "AutoInitStore"; + case RK_Unknown: + return "AutoInitUnknownInstruction"; + case RK_IntrinsicCall: + return "AutoInitIntrinsicCall"; + case RK_Call: + return "AutoInitCall"; + } +} diff --git a/llvm/test/CodeGen/AArch64/memsize-remarks.ll b/llvm/test/CodeGen/AArch64/memsize-remarks.ll new file mode 100644 index 000000000000..db4f9f4bd453 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/memsize-remarks.ll @@ -0,0 +1,373 @@ +; RUN: llc %s -pass-remarks-missed=memsize -pass-remarks-output=%t.opt.yaml -pass-remarks-filter=memsize -global-isel -o /dev/null 2>&1 | FileCheck %s --check-prefix=GISEL --implicit-check-not=GISEL +; RUN: cat %t.opt.yaml | FileCheck -check-prefix=YAML %s + +source_filename = "memsize.c" +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-ios7.0.0" + +declare i8* @__memmove_chk(i8*, i8*, i64, i64) #1 +declare i8* @__memcpy_chk(i8*, i8*, i64, i64) #1 +declare i8* @__memset_chk(i8*, i32, i64, i64) #1 +declare i64 @llvm.objectsize.i64.p0i8(i8*, i1 immarg, i1 immarg, i1 immarg) #2 +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) argmemonly nounwind willreturn writeonly +declare void @llvm.memmove.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i1 immarg) argmemonly nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) argmemonly nounwind willreturn +declare void @bzero(i8* nocapture, i64) nofree nounwind +declare void @bcopy(i8* nocapture, i8* nocapture, i64) nofree nounwind +declare i8* @memset(i8*, i32, i64) + +define void @memcpy_dynamic(i8* %d, i8* %s, i64 %l) #0 !dbg !14 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !16 +; GISEL: remark: memsize.c:4:3: Call to memcpy.{{$}} + %call = call i8* @__memcpy_chk(i8* %d, i8* %s, i64 %l, i64 %0) #4, !dbg !17 + ret void, !dbg !18 +} + +define void @memcpy_single(i8* %d, i8* %s, i64 %l) #0 !dbg !23 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !24 +; GISEL: remark: memsize.c:10:3: Call to memcpy. Memory operation size: 1 bytes. + %call = call i8* @__memcpy_chk(i8* %d, i8* %s, i64 1, i64 %0) #4, !dbg !25 + ret void, !dbg !26 +} + +define void @memcpy_intrinsic(i8* %d, i8* %s, i64 %l) #0 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false) +; GISEL: remark: :0:0: Call to memcpy. Memory operation size: 1 bytes. + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %d, i8* %s, i64 1, i1 false) + ret void +} + +define void @memcpy_static(i8* %d, i8* %s, i64 %l) #0 !dbg !27 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !28 +; GISEL: remark: memsize.c:13:3: Call to memcpy. Memory operation size: 100 bytes. + %call = call i8* @__memcpy_chk(i8* %d, i8* %s, i64 100, i64 %0) #4, !dbg !29 + ret void, !dbg !30 +} + +define void @memcpy_huge(i8* %d, i8* %s, i64 %l) #0 !dbg !31 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !32 +; GISEL: remark: memsize.c:16:3: Call to memcpy. Memory operation size: 100000 bytes. + %call = call i8* @__memcpy_chk(i8* %d, i8* %s, i64 100000, i64 %0) #4, !dbg !33 + ret void, !dbg !34 +} + +define void @memmove_dynamic(i8* %d, i8* %s, i64 %l) #0 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false) +; GISEL: remark: :0:0: Call to memmove.{{$}} + %call = call i8* @__memmove_chk(i8* %d, i8* %s, i64 %l, i64 %0) #4 + ret void +} + +define void @memmove_single(i8* %d, i8* %s, i64 %l) #0 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false) +; GISEL: remark: :0:0: Call to memmove. Memory operation size: 1 bytes. + %call = call i8* @__memmove_chk(i8* %d, i8* %s, i64 1, i64 %0) #4 + ret void +} + +define void @memmove_static(i8* %d, i8* %s, i64 %l) #0 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false) +; GISEL: remark: :0:0: Call to memmove. Memory operation size: 100 bytes. + %call = call i8* @__memmove_chk(i8* %d, i8* %s, i64 100, i64 %0) #4 + ret void +} + +define void @memmove_huge(i8* %d, i8* %s, i64 %l) #0 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false) +; GISEL: remark: :0:0: Call to memmove. Memory operation size: 100000 bytes. + %call = call i8* @__memmove_chk(i8* %d, i8* %s, i64 100000, i64 %0) #4 + ret void +} + +define void @memset_dynamic(i8* %d, i64 %l) #0 !dbg !38 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !39 +; GISEL: remark: memsize.c:22:3: Call to memset.{{$}} + %call = call i8* @__memset_chk(i8* %d, i32 0, i64 %l, i64 %0) #4, !dbg !40 + ret void, !dbg !41 +} + +define void @memset_single(i8* %d, i64 %l) #0 !dbg !46 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !47 +; GISEL: remark: memsize.c:28:3: Call to memset. Memory operation size: 1 bytes. + %call = call i8* @__memset_chk(i8* %d, i32 0, i64 1, i64 %0) #4, !dbg !48 + ret void, !dbg !49 +} + +define void @memset_static(i8* %d, i64 %l) #0 !dbg !50 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !51 +; GISEL: remark: memsize.c:31:3: Call to memset. Memory operation size: 100 bytes. + %call = call i8* @__memset_chk(i8* %d, i32 0, i64 100, i64 %0) #4, !dbg !52 + ret void, !dbg !53 +} + +define void @memset_huge(i8* %d, i64 %l) #0 !dbg !54 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !55 +; GISEL: remark: memsize.c:34:3: Call to memset. Memory operation size: 100000 bytes. + %call = call i8* @__memset_chk(i8* %d, i32 0, i64 100000, i64 %0) #4, !dbg !56 + ret void, !dbg !57 +} + +define void @memset_empty(i8* %d, i64 %l) #0 !dbg !42 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !43 +; GISEL: remark: memsize.c:25:3: Call to memset. Memory operation size: 0 bytes. + %call = call i8* @__memset_chk(i8* %d, i32 0, i64 0, i64 %0) #4, !dbg !44 + ret void, !dbg !45 +} + +; YAML-LABEL: Function: memcpy_empty +define void @memcpy_empty(i8* %d, i8* %s, i64 %l) #0 !dbg !19 { +entry: + %0 = call i64 @llvm.objectsize.i64.p0i8(i8* %d, i1 false, i1 true, i1 false), !dbg !20 +; GISEL: remark: memsize.c:7:3: Call to memcpy. Memory operation size: 0 bytes. + %call = call i8* @__memcpy_chk(i8* %d, i8* %s, i64 0, i64 %0) #4, !dbg !21 + ret void, !dbg !22 +} + +; Emit remarks for memcpy, memmove, memset, bzero, bcopy with known constant +; sizes to an object of known size. +define void @known_call_with_dereferenceable_bytes(i8* dereferenceable(42) %dst, i8* dereferenceable(314) %src) { +; GISEL: Call to memset. Memory operation size: 1 bytes. +; GISEL-NOT: Read Variables: +; GISEL-NEXT: Written Variables: (42 bytes). +; YAML: --- !Missed +; YAML: Pass: memsize +; YAML: Name: MemoryOpIntrinsicCall +; YAML-LABEL: Function: known_call_with_dereferenceable_bytes +; YAML-NEXT: Args: +; YAML-NEXT: - String: 'Call to ' +; YAML-NEXT: - Callee: memset +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Memory operation size: ' +; YAML-NEXT: - StoreSize: '1' +; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - WVarSize: '42' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Volatile: ' +; YAML-NEXT: - StoreVolatile: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Atomic: ' +; YAML-NEXT: - StoreAtomic: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: ... + call void @llvm.memset.p0i8.i64(i8* %dst, i8 0, i64 1, i1 false) + +; GISEL: Call to memcpy. Memory operation size: 1 bytes. +; GISEL-NEXT: Read Variables: (314 bytes). +; GISEL-NEXT: Written Variables: (42 bytes). +; YAML: --- !Missed +; YAML: Pass: memsize +; YAML: Name: MemoryOpIntrinsicCall +; YAML-LABEL: Function: known_call_with_dereferenceable_bytes +; YAML-NEXT: Args: +; YAML-NEXT: - String: 'Call to ' +; YAML-NEXT: - Callee: memcpy +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Memory operation size: ' +; YAML-NEXT: - StoreSize: '1' +; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: "\nRead Variables: " +; YAML-NEXT: - RVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - RVarSize: '314' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - WVarSize: '42' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Volatile: ' +; YAML-NEXT: - StoreVolatile: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Atomic: ' +; YAML-NEXT: - StoreAtomic: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: ... + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 1, i1 false) + +; GISEL: Call to memmove. Memory operation size: 1 bytes. +; GISEL-NEXT: Read Variables: (314 bytes). +; GISEL-NEXT: Written Variables: (42 bytes). +; YAML: --- !Missed +; YAML: Pass: memsize +; YAML: Name: MemoryOpIntrinsicCall +; YAML-LABEL: Function: known_call_with_dereferenceable_bytes +; YAML-NEXT: Args: +; YAML-NEXT: - String: 'Call to ' +; YAML-NEXT: - Callee: memmove +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Memory operation size: ' +; YAML-NEXT: - StoreSize: '1' +; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: "\nRead Variables: " +; YAML-NEXT: - RVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - RVarSize: '314' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - WVarSize: '42' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Volatile: ' +; YAML-NEXT: - StoreVolatile: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Atomic: ' +; YAML-NEXT: - StoreAtomic: 'false' +; YAML-NEXT: - String: . +; YAML-NEXT: ... + call void @llvm.memmove.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 1, i1 false) + +; GISEL: Call to bzero. Memory operation size: 1 bytes. +; GISEL-NOT: Read Variables: +; GISEL-NEXT: Written Variables: (42 bytes). +; YAML: --- !Missed +; YAML: Pass: memsize +; YAML: Name: MemoryOpCall +; YAML-LABEL: Function: known_call_with_dereferenceable_bytes +; YAML-NEXT: Args: +; YAML-NEXT: - String: 'Call to ' +; YAML-NEXT: - Callee: bzero +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Memory operation size: ' +; YAML-NEXT: - StoreSize: '1' +; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - WVarSize: '42' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: ... + call void @bzero(i8* %dst, i64 1) + +; GISEL: Call to bcopy. Memory operation size: 1 bytes. +; GISEL-NEXT: Read Variables: (314 bytes). +; GISEL-NEXT: Written Variables: (42 bytes). +; YAML: --- !Missed +; YAML: Pass: memsize +; YAML: Name: MemoryOpCall +; YAML-LABEL: Function: known_call_with_dereferenceable_bytes +; YAML-NEXT: Args: +; YAML-NEXT: - String: 'Call to ' +; YAML-NEXT: - Callee: bcopy +; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Memory operation size: ' +; YAML-NEXT: - StoreSize: '1' +; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: "\nRead Variables: " +; YAML-NEXT: - RVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - RVarSize: '314' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: '' +; YAML-NEXT: - String: ' (' +; YAML-NEXT: - WVarSize: '42' +; YAML-NEXT: - String: ' bytes)' +; YAML-NEXT: - String: . +; YAML-NEXT: ... + call void @bcopy(i8* %dst, i8* %src, i64 1) + ret void +} + +attributes #0 = { noinline nounwind ssp uwtable "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-a7" "target-features"="+aes,+crypto,+fp-armv8,+neon,+sha2,+zcm,+zcz" } +attributes #1 = { nounwind "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-a7" "target-features"="+aes,+crypto,+fp-armv8,+neon,+sha2,+zcm,+zcz" } +attributes #2 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #3 = { argmemonly nofree nosync nounwind willreturn } +attributes #4 = { nounwind } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9} +!llvm.dbg.cu = !{!10} +!llvm.ident = !{!13} + +!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 12, i32 0]} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = !{i32 1, !"wchar_size", i32 4} +!3 = !{i32 1, !"branch-target-enforcement", i32 0} +!4 = !{i32 1, !"sign-return-address", i32 0} +!5 = !{i32 1, !"sign-return-address-all", i32 0} +!6 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!7 = !{i32 7, !"PIC Level", i32 2} +!8 = !{i32 7, !"uwtable", i32 1} +!9 = !{i32 7, !"frame-pointer", i32 1} +!10 = distinct !DICompileUnit(language: DW_LANG_C99, file: !11, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: NoDebug, enums: !12, splitDebugInlining: false, nameTableKind: None, sysroot: "/") +!11 = !DIFile(filename: "memsize.c", directory: "") +!12 = !{} +!13 = !{!"clang"} +!14 = distinct !DISubprogram(name: "memcpy_dynamic", scope: !11, file: !11, line: 3, type: !15, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!15 = !DISubroutineType(types: !12) +!16 = !DILocation(line: 4, column: 36, scope: !14) +!17 = !DILocation(line: 4, column: 3, scope: !14) +!18 = !DILocation(line: 5, column: 1, scope: !14) +!19 = distinct !DISubprogram(name: "memcpy_empty", scope: !11, file: !11, line: 6, type: !15, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!20 = !DILocation(line: 7, column: 36, scope: !19) +!21 = !DILocation(line: 7, column: 3, scope: !19) +!22 = !DILocation(line: 8, column: 1, scope: !19) +!23 = distinct !DISubprogram(name: "memcpy_single", scope: !11, file: !11, line: 9, type: !15, scopeLine: 9, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!24 = !DILocation(line: 10, column: 36, scope: !23) +!25 = !DILocation(line: 10, column: 3, scope: !23) +!26 = !DILocation(line: 11, column: 1, scope: !23) +!27 = distinct !DISubprogram(name: "memcpy_static", scope: !11, file: !11, line: 12, type: !15, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!28 = !DILocation(line: 13, column: 38, scope: !27) +!29 = !DILocation(line: 13, column: 3, scope: !27) +!30 = !DILocation(line: 14, column: 1, scope: !27) +!31 = distinct !DISubprogram(name: "memcpy_huge", scope: !11, file: !11, line: 15, type: !15, scopeLine: 15, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!32 = !DILocation(line: 16, column: 41, scope: !31) +!33 = !DILocation(line: 16, column: 3, scope: !31) +!34 = !DILocation(line: 17, column: 1, scope: !31) +!35 = distinct !DISubprogram(name: "memcpy_inline", scope: !11, file: !11, line: 18, type: !15, scopeLine: 18, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!36 = !DILocation(line: 19, column: 3, scope: !35) +!37 = !DILocation(line: 20, column: 1, scope: !35) +!38 = distinct !DISubprogram(name: "memset_dynamic", scope: !11, file: !11, line: 21, type: !15, scopeLine: 21, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!39 = !DILocation(line: 22, column: 36, scope: !38) +!40 = !DILocation(line: 22, column: 3, scope: !38) +!41 = !DILocation(line: 23, column: 1, scope: !38) +!42 = distinct !DISubprogram(name: "memset_empty", scope: !11, file: !11, line: 24, type: !15, scopeLine: 24, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!43 = !DILocation(line: 25, column: 36, scope: !42) +!44 = !DILocation(line: 25, column: 3, scope: !42) +!45 = !DILocation(line: 26, column: 1, scope: !42) +!46 = distinct !DISubprogram(name: "memset_single", scope: !11, file: !11, line: 27, type: !15, scopeLine: 27, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!47 = !DILocation(line: 28, column: 36, scope: !46) +!48 = !DILocation(line: 28, column: 3, scope: !46) +!49 = !DILocation(line: 29, column: 1, scope: !46) +!50 = distinct !DISubprogram(name: "memset_static", scope: !11, file: !11, line: 30, type: !15, scopeLine: 30, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!51 = !DILocation(line: 31, column: 38, scope: !50) +!52 = !DILocation(line: 31, column: 3, scope: !50) +!53 = !DILocation(line: 32, column: 1, scope: !50) +!54 = distinct !DISubprogram(name: "memset_huge", scope: !11, file: !11, line: 33, type: !15, scopeLine: 33, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) +!55 = !DILocation(line: 34, column: 41, scope: !54) +!56 = !DILocation(line: 34, column: 3, scope: !54) +!57 = !DILocation(line: 35, column: 1, scope: !54) +!58 = distinct !DISubprogram(name: "auto_init", scope: !11, file: !11, line: 37, type: !15, scopeLine: 37, spFlags: DISPFlagDefinition, unit: !10, retainedNodes: !12) diff --git a/llvm/test/Transforms/Util/trivial-auto-var-init-call.ll b/llvm/test/Transforms/Util/trivial-auto-var-init-call.ll index 79c69bd44082..288ea3521199 100644 --- a/llvm/test/Transforms/Util/trivial-auto-var-init-call.ll +++ b/llvm/test/Transforms/Util/trivial-auto-var-init-call.ll @@ -13,6 +13,9 @@ define void @known_call(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: 'Call to ' ; YAML-NEXT: - Callee: memset ; YAML-NEXT: - String: ' inserted by -ftrivial-auto-var-init.' +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -31,6 +34,9 @@ define void @known_call(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: 'Call to ' ; YAML-NEXT: - Callee: memcpy ; YAML-NEXT: - String: ' inserted by -ftrivial-auto-var-init.' +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -49,6 +55,9 @@ define void @known_call(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: 'Call to ' ; YAML-NEXT: - Callee: memmove ; YAML-NEXT: - String: ' inserted by -ftrivial-auto-var-init.' +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -79,6 +88,9 @@ define void @known_call(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: 'Call to ' ; YAML-NEXT: - Callee: memset ; YAML-NEXT: - String: ' inserted by -ftrivial-auto-var-init.' +; YAML-NEXT: - String: ' Memory operation size: ' +; YAML-NEXT: - StoreSize: '32' +; YAML-NEXT: - String: ' bytes.' ; YAML-NEXT: ... call i8* @memset(i8* %dst, i32 0, i64 32), !annotation !0, !dbg !DILocation(scope: !4) ret void @@ -99,6 +111,9 @@ define void @known_call_with_size(i8* %src, i8* %dst) { ; YAML-NEXT: - String: ' Memory operation size: ' ; YAML-NEXT: - StoreSize: '32' ; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -120,6 +135,9 @@ define void @known_call_with_size(i8* %src, i8* %dst) { ; YAML-NEXT: - String: ' Memory operation size: ' ; YAML-NEXT: - StoreSize: '32' ; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -141,6 +159,9 @@ define void @known_call_with_size(i8* %src, i8* %dst) { ; YAML-NEXT: - String: ' Memory operation size: ' ; YAML-NEXT: - StoreSize: '32' ; YAML-NEXT: - String: ' bytes.' +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -183,6 +204,9 @@ define void @known_call_volatile(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'true' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Atomic: ' ; YAML-NEXT: - StoreAtomic: 'false' ; YAML-NEXT: - String: . @@ -201,6 +225,9 @@ define void @known_call_volatile(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'true' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Atomic: ' ; YAML-NEXT: - StoreAtomic: 'false' ; YAML-NEXT: - String: . @@ -219,6 +246,9 @@ define void @known_call_volatile(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'true' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Atomic: ' ; YAML-NEXT: - StoreAtomic: 'false' ; YAML-NEXT: - String: . @@ -242,6 +272,9 @@ define void @known_call_atomic(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: ' Atomic: ' ; YAML-NEXT: - StoreAtomic: 'true' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -260,6 +293,9 @@ define void @known_call_atomic(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: ' Atomic: ' ; YAML-NEXT: - StoreAtomic: 'true' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -278,6 +314,9 @@ define void @known_call_atomic(i8* %src, i8* %dst, i64 %size) { ; YAML-NEXT: - String: ' Atomic: ' ; YAML-NEXT: - StoreAtomic: 'true' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -303,12 +342,15 @@ define void @known_call_with_size_alloca(i8* %src) { ; YAML-NEXT: - String: ' Memory operation size: ' ; YAML-NEXT: - StoreSize: '1' ; YAML-NEXT: - String: ' bytes.' -; YAML-NEXT: - String: "\nVariables: " -; YAML-NEXT: - VarName: dst +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: dst ; YAML-NEXT: - String: ' (' -; YAML-NEXT: - VarSize: '1' +; YAML-NEXT: - WVarSize: '1' ; YAML-NEXT: - String: ' bytes)' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -332,12 +374,15 @@ define void @known_call_with_size_alloca(i8* %src) { ; YAML-NEXT: - String: ' Memory operation size: ' ; YAML-NEXT: - StoreSize: '1' ; YAML-NEXT: - String: ' bytes.' -; YAML-NEXT: - String: "\nVariables: " -; YAML-NEXT: - VarName: dst +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: dst ; YAML-NEXT: - String: ' (' -; YAML-NEXT: - VarSize: '1' +; YAML-NEXT: - WVarSize: '1' ; YAML-NEXT: - String: ' bytes)' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -360,12 +405,15 @@ define void @known_call_with_size_alloca(i8* %src) { ; YAML-NEXT: - String: ' Memory operation size: ' ; YAML-NEXT: - StoreSize: '1' ; YAML-NEXT: - String: ' bytes.' -; YAML-NEXT: - String: "\nVariables: " -; YAML-NEXT: - VarName: dst +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: dst ; YAML-NEXT: - String: ' (' -; YAML-NEXT: - VarSize: '1' +; YAML-NEXT: - WVarSize: '1' ; YAML-NEXT: - String: ' bytes)' ; YAML-NEXT: - String: . +; YAML-NEXT: - String: ' Inlined: ' +; YAML-NEXT: - StoreInlined: 'false' +; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: ' ; YAML-NEXT: - StoreVolatile: 'false' ; YAML-NEXT: - String: . @@ -388,10 +436,10 @@ define void @known_call_with_size_alloca(i8* %src) { ; YAML-NEXT: - String: ' Memory operation size: ' ; YAML-NEXT: - StoreSize: '1' ; YAML-NEXT: - String: ' bytes.' -; YAML-NEXT: - String: "\nVariables: " -; YAML-NEXT: - VarName: dst +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: dst ; YAML-NEXT: - String: ' (' -; YAML-NEXT: - VarSize: '1' +; YAML-NEXT: - WVarSize: '1' ; YAML-NEXT: - String: ' bytes)' ; YAML-NEXT: - String: . ; YAML-NEXT: ... diff --git a/llvm/test/Transforms/Util/trivial-auto-var-init-store.ll b/llvm/test/Transforms/Util/trivial-auto-var-init-store.ll index 2286d0070f4f..5172e9440acf 100644 --- a/llvm/test/Transforms/Util/trivial-auto-var-init-store.ll +++ b/llvm/test/Transforms/Util/trivial-auto-var-init-store.ll @@ -11,7 +11,8 @@ define void @store(i32* %dst) { ; YAML-NEXT: DebugLoc: ; YAML-NEXT: Function: store ; YAML-NEXT: Args: -; YAML-NEXT: - String: "Store inserted by -ftrivial-auto-var-init.\nStore size: " +; YAML-NEXT: - String: Store inserted by -ftrivial-auto-var-init. +; YAML-NEXT: - String: "\nStore size: " ; YAML-NEXT: - StoreSize: '4' ; YAML-NEXT: - String: ' bytes.' ; YAML-NEXT: - String: ' Volatile: ' @@ -35,7 +36,8 @@ define void @volatile_store(i32* %dst) { ; YAML-NEXT: DebugLoc: ; YAML-NEXT: Function: volatile_store ; YAML-NEXT: Args: -; YAML-NEXT: - String: "Store inserted by -ftrivial-auto-var-init.\nStore size: " +; YAML-NEXT: - String: Store inserted by -ftrivial-auto-var-init. +; YAML-NEXT: - String: "\nStore size: " ; YAML-NEXT: - StoreSize: '4' ; YAML-NEXT: - String: ' bytes.' ; YAML-NEXT: - String: ' Volatile: ' @@ -59,7 +61,8 @@ define void @atomic_store(i32* %dst) { ; YAML-NEXT: DebugLoc: ; YAML-NEXT: Function: atomic_store ; YAML-NEXT: Args: -; YAML-NEXT: - String: "Store inserted by -ftrivial-auto-var-init.\nStore size: " +; YAML-NEXT: - String: Store inserted by -ftrivial-auto-var-init. +; YAML-NEXT: - String: "\nStore size: " ; YAML-NEXT: - StoreSize: '4' ; YAML-NEXT: - String: ' bytes.' ; YAML-NEXT: - String: ' Atomic: ' @@ -84,13 +87,14 @@ define void @store_alloca() { ; YAML-NEXT: DebugLoc: ; YAML-NEXT: Function: store_alloca ; YAML-NEXT: Args: -; YAML-NEXT: - String: "Store inserted by -ftrivial-auto-var-init.\nStore size: " +; YAML-NEXT: - String: Store inserted by -ftrivial-auto-var-init. +; YAML-NEXT: - String: "\nStore size: " ; YAML-NEXT: - StoreSize: '4' ; YAML-NEXT: - String: ' bytes.' -; YAML-NEXT: - String: "\nVariables: " -; YAML-NEXT: - VarName: dst +; YAML-NEXT: - String: "\nWritten Variables: " +; YAML-NEXT: - WVarName: dst ; YAML-NEXT: - String: ' (' -; YAML-NEXT: - VarSize: '4' +; YAML-NEXT: - WVarSize: '4' ; YAML-NEXT: - String: ' bytes)' ; YAML-NEXT: - String: . ; YAML-NEXT: - String: ' Volatile: '