blob: 0c72ee5b01e94ec7f857e1bc92d7f08d869a5857 [file] [log] [blame]
//===-- 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<uint64_t> getSizeInBytes(Optional<uint64_t> 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<ConstantInt>(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 <typename FTy>
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<ConstantInt>(V)) {
uint64_t Size = Len->getZExtValue();
R << " Memory operation size: " << NV("StoreSize", Size) << " bytes.";
}
}
void AutoInitRemark::inspectVariable(const Value *V,
SmallVectorImpl<VariableInfo> &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<Value *>(V))) {
if (DILocalVariable *DILV = DVI->getVariable()) {
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.
Optional<StringRef> Name = AI->hasName()
? Optional<StringRef>(AI->getName())
: Optional<StringRef>(None);
Optional<TypeSize> TySize = AI->getAllocationSizeInBits(DL);
Optional<uint64_t> 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<const Value *, 2> Objects;
getUnderlyingObjects(Dst, Objects);
SmallVector<VariableInfo, 2> 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", "<unknown>");
if (VI.Size)
R << " (" << NV("VarSize", *VI.Size) << " bytes)";
}
R << ".";
}