mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 02:33:06 +01:00
e29819ce26
Instead of getType()->getElementType().
409 lines
12 KiB
C++
409 lines
12 KiB
C++
//===-- 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<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 Optional<uint64_t> getSizeInBytes(Optional<uint64_t> SizeInBits) {
|
|
if (!SizeInBits || *SizeInBits % 8 != 0)
|
|
return None;
|
|
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 Optional<StringRef> nameOrNone(const Value *V) {
|
|
if (V->hasName())
|
|
return V->getName();
|
|
return None;
|
|
}
|
|
|
|
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).getFixedSize();
|
|
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()) {
|
|
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<TypeSize> TySize = AI->getAllocationSizeInBits(DL);
|
|
Optional<uint64_t> Size =
|
|
TySize ? getSizeInBytes(TySize->getFixedSize()) : None;
|
|
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({None, 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");
|
|
}
|