Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "llvm/IR/Intrinsics.h"
#include <cassert>
#include <cstdint>
#include <variant>

namespace llvm {

Expand Down Expand Up @@ -1195,6 +1196,23 @@ std::optional<bool> isImpliedByDomCondition(CmpInst::Predicate Pred,
const Value *LHS, const Value *RHS,
const Instruction *ContextI,
const DataLayout &DL);

/// A helper class to see whether we will lose information (KnownBits,
/// KnownFPClass...) after replacing all uses of \p From to \p To . It will help
/// us salvage information during transformation.
class ValueTrackingCache final {
Instruction *From;

bool NoPoison;
bool NoUndef;

std::variant<std::monostate, KnownBits, KnownFPClass> BeforeKnown;

public:
explicit ValueTrackingCache(Instruction *FromInst, const SimplifyQuery &SQ);
void detectInformationLoss(Value *To, const SimplifyQuery &SQ);
Instruction *getFromInst() const noexcept { return From; }
};
} // end namespace llvm

#endif // LLVM_ANALYSIS_VALUETRACKING_H
9 changes: 9 additions & 0 deletions llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
/// Order of predecessors to canonicalize phi nodes towards.
SmallDenseMap<BasicBlock *, SmallVector<BasicBlock *>, 8> PredOrder;

/// ValueTrackingCache is used for detecting information loss.
#ifndef NDEBUG
ValueTrackingCache *VTC = nullptr;
#endif

public:
InstCombiner(InstructionWorklist &Worklist, BuilderTy &Builder,
bool MinimizeSize, AAResults *AA, AssumptionCache &AC,
Expand Down Expand Up @@ -402,6 +407,10 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
V->takeName(&I);

I.replaceAllUsesWith(V);
#ifndef NDEBUG
if (&I == VTC->getFromInst())
VTC->detectInformationLoss(V, SQ);
#endif
return &I;
}

Expand Down
102 changes: 102 additions & 0 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
#include <cstdint>
#include <optional>
#include <utility>
#include <variant>

using namespace llvm;
using namespace llvm::PatternMatch;
Expand All @@ -87,6 +88,9 @@ using namespace llvm::PatternMatch;
static cl::opt<unsigned> DomConditionsMaxUses("dom-conditions-max-uses",
cl::Hidden, cl::init(20));

// Checks whether we will lose information after simplification.
static cl::opt<bool> DetectInformationLoss("detect-information-loss",
cl::Hidden, cl::init(false));

/// Returns the bitwidth of the given scalar or pointer type. For vector types,
/// returns the element type's bitwidth.
Expand Down Expand Up @@ -9053,3 +9057,101 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,

return CR;
}

#ifndef NDEBUG
llvm::ValueTrackingCache::ValueTrackingCache(Instruction *FromInst,
const SimplifyQuery &SQ)
: From(FromInst) {
if (!DetectInformationLoss)
return;

NoPoison = isGuaranteedNotToBePoison(From, SQ.AC, From, SQ.DT);
NoUndef = isGuaranteedNotToBeUndef(From, SQ.AC, From, SQ.DT);

Type *Ty = From->getType();
if (Ty->isIntOrIntVectorTy() || Ty->isPtrOrPtrVectorTy()) {
// KnownBits
KnownBits Known =
computeKnownBits(From, /*Depth=*/0, SQ.getWithInstruction(From));
if (!Known.isUnknown())
BeforeKnown = Known;
} else if (Ty->isFPOrFPVectorTy()) {
// KnownFPClass
// TODO: use FMF flags
KnownFPClass Known = computeKnownFPClass(From, fcAllFlags, /*Depth=*/0,
SQ.getWithInstruction(From));
if (Known.KnownFPClasses != fcAllFlags || Known.SignBit)
BeforeKnown = Known;
}
}

void llvm::ValueTrackingCache::detectInformationLoss(Value *To,
const SimplifyQuery &SQ) {
if (!DetectInformationLoss)
return;

Instruction *ToInst = dyn_cast<Instruction>(To);
if (!ToInst)
return;

bool Inserted = false;
if (!ToInst->getParent()) {
ToInst->insertBefore(From);
Inserted = true;
}

auto WarnOnInformationLoss = [&](StringRef Attr) {
errs() << "Warning: the attribute " << Attr << " got lost when simplifying "
<< *From << " into " << *To << '\n';
};

// Poison
if (NoPoison && !isGuaranteedNotToBePoison(To, SQ.AC, From, SQ.DT))
WarnOnInformationLoss("non-poison");

// Undef
if (NoUndef && !isGuaranteedNotToBeUndef(To, SQ.AC, From, SQ.DT))
WarnOnInformationLoss("non-undef");

Type *Ty = From->getType();
if ((Ty->isIntOrIntVectorTy() || Ty->isPtrOrPtrVectorTy()) &&
std::holds_alternative<KnownBits>(BeforeKnown)) {
KnownBits &Before = std::get<KnownBits>(BeforeKnown);
KnownBits After =
computeKnownBits(To, /*Depth=*/0, SQ.getWithInstruction(From));
// KnownBits of From should be a subset of KnownBits of To.
if (!Before.Zero.isSubsetOf(After.Zero) ||
!Before.One.isSubsetOf(After.One)) {
WarnOnInformationLoss("knownbits");
errs() << "Before: " << Before << '\n';
errs() << "After: " << After << '\n';
}
assert((Before.One & After.Zero).isZero() && "Possible miscompilation");
assert((Before.Zero & After.One).isZero() && "Possible miscompilation");
} else if (Ty->isFPOrFPVectorTy() &&
std::holds_alternative<KnownFPClass>(BeforeKnown)) {
// KnownFPClass
KnownFPClass &Before = std::get<KnownFPClass>(BeforeKnown);
// TODO: use FMF flags
KnownFPClass After = computeKnownFPClass(To, fcAllFlags, /*Depth=*/0,
SQ.getWithInstruction(From));
// KnownFPClass of From should be a subset of KnownFPClass of To.
if ((Before.KnownFPClasses & After.KnownFPClasses) !=
Before.KnownFPClasses) {
WarnOnInformationLoss("fpclasses");
errs() << "Before: " << Before.KnownFPClasses << '\n';
errs() << "After: " << After.KnownFPClasses << '\n';
}
assert((Before.KnownFPClasses & After.KnownFPClasses) != fcNone &&
"Possible miscompilation");
if (Before.SignBit.has_value() && !After.SignBit.has_value())
WarnOnInformationLoss("sign");
assert((!Before.SignBit.has_value() || !After.SignBit.has_value() ||
Before.SignBit == After.SignBit) &&
"Possible miscompilation");
}

if (Inserted)
ToInst->removeFromParent();
}
#endif
7 changes: 7 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4683,6 +4683,10 @@ bool InstCombinerImpl::run() {
#endif
LLVM_DEBUG(raw_string_ostream SS(OrigI); I->print(SS); OrigI = SS.str(););
LLVM_DEBUG(dbgs() << "IC: Visiting: " << OrigI << '\n');
#ifndef NDEBUG
ValueTrackingCache Cache(I, SQ);
VTC = &Cache;
#endif

if (Instruction *Result = visit(*I)) {
++NumCombined;
Expand Down Expand Up @@ -4718,6 +4722,9 @@ bool InstCombinerImpl::run() {
Worklist.pushUsersToWorkList(*Result);
Worklist.push(Result);

#ifndef NDEBUG
Cache.detectInformationLoss(Result, SQ);
#endif
eraseInstFromFunction(*I);
} else {
LLVM_DEBUG(dbgs() << "IC: Mod = " << OrigI << '\n'
Expand Down