//===- ValueLattice.h - Value constraint analysis ---------------*- C++ -*-===//
 
//
 
// 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
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_ANALYSIS_VALUELATTICE_H
 
#define LLVM_ANALYSIS_VALUELATTICE_H
 
 
 
#include "llvm/IR/Constants.h"
 
#include "llvm/IR/ConstantRange.h"
 
#include "llvm/IR/Instructions.h"
 
 
 
//===----------------------------------------------------------------------===//
 
//                               ValueLatticeElement
 
//===----------------------------------------------------------------------===//
 
 
 
namespace llvm {
 
 
 
class Constant;
 
 
 
/// This class represents lattice values for constants.
 
///
 
/// FIXME: This is basically just for bringup, this can be made a lot more rich
 
/// in the future.
 
///
 
class ValueLatticeElement {
 
  enum ValueLatticeElementTy {
 
    /// This Value has no known value yet.  As a result, this implies the
 
    /// producing instruction is dead.  Caution: We use this as the starting
 
    /// state in our local meet rules.  In this usage, it's taken to mean
 
    /// "nothing known yet".
 
    /// Transition to any other state allowed.
 
    unknown,
 
 
 
    /// This Value is an UndefValue constant or produces undef. Undefined values
 
    /// can be merged with constants (or single element constant ranges),
 
    /// assuming all uses of the result will be replaced.
 
    /// Transition allowed to the following states:
 
    ///  constant
 
    ///  constantrange_including_undef
 
    ///  overdefined
 
    undef,
 
 
 
    /// This Value has a specific constant value.  The constant cannot be undef.
 
    /// (For constant integers, constantrange is used instead. Integer typed
 
    /// constantexprs can appear as constant.) Note that the constant state
 
    /// can be reached by merging undef & constant states.
 
    /// Transition allowed to the following states:
 
    ///  overdefined
 
    constant,
 
 
 
    /// This Value is known to not have the specified value. (For constant
 
    /// integers, constantrange is used instead.  As above, integer typed
 
    /// constantexprs can appear here.)
 
    /// Transition allowed to the following states:
 
    ///  overdefined
 
    notconstant,
 
 
 
    /// The Value falls within this range. (Used only for integer typed values.)
 
    /// Transition allowed to the following states:
 
    ///  constantrange (new range must be a superset of the existing range)
 
    ///  constantrange_including_undef
 
    ///  overdefined
 
    constantrange,
 
 
 
    /// This Value falls within this range, but also may be undef.
 
    /// Merging it with other constant ranges results in
 
    /// constantrange_including_undef.
 
    /// Transition allowed to the following states:
 
    ///  overdefined
 
    constantrange_including_undef,
 
 
 
    /// We can not precisely model the dynamic values this value might take.
 
    /// No transitions are allowed after reaching overdefined.
 
    overdefined,
 
  };
 
 
 
  ValueLatticeElementTy Tag : 8;
 
  /// Number of times a constant range has been extended with widening enabled.
 
  unsigned NumRangeExtensions : 8;
 
 
 
  /// The union either stores a pointer to a constant or a constant range,
 
  /// associated to the lattice element. We have to ensure that Range is
 
  /// initialized or destroyed when changing state to or from constantrange.
 
  union {
 
    Constant *ConstVal;
 
    ConstantRange Range;
 
  };
 
 
 
  /// Destroy contents of lattice value, without destructing the object.
 
  void destroy() {
 
    switch (Tag) {
 
    case overdefined:
 
    case unknown:
 
    case undef:
 
    case constant:
 
    case notconstant:
 
      break;
 
    case constantrange_including_undef:
 
    case constantrange:
 
      Range.~ConstantRange();
 
      break;
 
    };
 
  }
 
 
 
public:
 
  /// Struct to control some aspects related to merging constant ranges.
 
  struct MergeOptions {
 
    /// The merge value may include undef.
 
    bool MayIncludeUndef;
 
 
 
    /// Handle repeatedly extending a range by going to overdefined after a
 
    /// number of steps.
 
    bool CheckWiden;
 
 
 
    /// The number of allowed widening steps (including setting the range
 
    /// initially).
 
    unsigned MaxWidenSteps;
 
 
 
    MergeOptions() : MergeOptions(false, false) {}
 
 
 
    MergeOptions(bool MayIncludeUndef, bool CheckWiden,
 
                 unsigned MaxWidenSteps = 1)
 
        : MayIncludeUndef(MayIncludeUndef), CheckWiden(CheckWiden),
 
          MaxWidenSteps(MaxWidenSteps) {}
 
 
 
    MergeOptions &setMayIncludeUndef(bool V = true) {
 
      MayIncludeUndef = V;
 
      return *this;
 
    }
 
 
 
    MergeOptions &setCheckWiden(bool V = true) {
 
      CheckWiden = V;
 
      return *this;
 
    }
 
 
 
    MergeOptions &setMaxWidenSteps(unsigned Steps = 1) {
 
      CheckWiden = true;
 
      MaxWidenSteps = Steps;
 
      return *this;
 
    }
 
  };
 
 
 
  // ConstVal and Range are initialized on-demand.
 
  ValueLatticeElement() : Tag(unknown), NumRangeExtensions(0) {}
 
 
 
  ~ValueLatticeElement() { destroy(); }
 
 
 
  ValueLatticeElement(const ValueLatticeElement &Other)
 
      : Tag(Other.Tag), NumRangeExtensions(0) {
 
    switch (Other.Tag) {
 
    case constantrange:
 
    case constantrange_including_undef:
 
      new (&Range) ConstantRange(Other.Range);
 
      NumRangeExtensions = Other.NumRangeExtensions;
 
      break;
 
    case constant:
 
    case notconstant:
 
      ConstVal = Other.ConstVal;
 
      break;
 
    case overdefined:
 
    case unknown:
 
    case undef:
 
      break;
 
    }
 
  }
 
 
 
  ValueLatticeElement(ValueLatticeElement &&Other)
 
      : Tag(Other.Tag), NumRangeExtensions(0) {
 
    switch (Other.Tag) {
 
    case constantrange:
 
    case constantrange_including_undef:
 
      new (&Range) ConstantRange(std::move(Other.Range));
 
      NumRangeExtensions = Other.NumRangeExtensions;
 
      break;
 
    case constant:
 
    case notconstant:
 
      ConstVal = Other.ConstVal;
 
      break;
 
    case overdefined:
 
    case unknown:
 
    case undef:
 
      break;
 
    }
 
    Other.Tag = unknown;
 
  }
 
 
 
  ValueLatticeElement &operator=(const ValueLatticeElement &Other) {
 
    destroy();
 
    new (this) ValueLatticeElement(Other);
 
    return *this;
 
  }
 
 
 
  ValueLatticeElement &operator=(ValueLatticeElement &&Other) {
 
    destroy();
 
    new (this) ValueLatticeElement(std::move(Other));
 
    return *this;
 
  }
 
 
 
  static ValueLatticeElement get(Constant *C) {
 
    ValueLatticeElement Res;
 
    if (isa<UndefValue>(C))
 
      Res.markUndef();
 
    else
 
      Res.markConstant(C);
 
    return Res;
 
  }
 
  static ValueLatticeElement getNot(Constant *C) {
 
    ValueLatticeElement Res;
 
    assert(!isa<UndefValue>(C) && "!= undef is not supported");
 
    Res.markNotConstant(C);
 
    return Res;
 
  }
 
  static ValueLatticeElement getRange(ConstantRange CR,
 
                                      bool MayIncludeUndef = false) {
 
    if (CR.isFullSet())
 
      return getOverdefined();
 
 
 
    if (CR.isEmptySet()) {
 
      ValueLatticeElement Res;
 
      if (MayIncludeUndef)
 
        Res.markUndef();
 
      return Res;
 
    }
 
 
 
    ValueLatticeElement Res;
 
    Res.markConstantRange(std::move(CR),
 
                          MergeOptions().setMayIncludeUndef(MayIncludeUndef));
 
    return Res;
 
  }
 
  static ValueLatticeElement getOverdefined() {
 
    ValueLatticeElement Res;
 
    Res.markOverdefined();
 
    return Res;
 
  }
 
 
 
  bool isUndef() const { return Tag == undef; }
 
  bool isUnknown() const { return Tag == unknown; }
 
  bool isUnknownOrUndef() const { return Tag == unknown || Tag == undef; }
 
  bool isConstant() const { return Tag == constant; }
 
  bool isNotConstant() const { return Tag == notconstant; }
 
  bool isConstantRangeIncludingUndef() const {
 
    return Tag == constantrange_including_undef;
 
  }
 
  /// Returns true if this value is a constant range. Use \p UndefAllowed to
 
  /// exclude non-singleton constant ranges that may also be undef. Note that
 
  /// this function also returns true if the range may include undef, but only
 
  /// contains a single element. In that case, it can be replaced by a constant.
 
  bool isConstantRange(bool UndefAllowed = true) const {
 
    return Tag == constantrange || (Tag == constantrange_including_undef &&
 
                                    (UndefAllowed || Range.isSingleElement()));
 
  }
 
  bool isOverdefined() const { return Tag == overdefined; }
 
 
 
  Constant *getConstant() const {
 
    assert(isConstant() && "Cannot get the constant of a non-constant!");
 
    return ConstVal;
 
  }
 
 
 
  Constant *getNotConstant() const {
 
    assert(isNotConstant() && "Cannot get the constant of a non-notconstant!");
 
    return ConstVal;
 
  }
 
 
 
  /// Returns the constant range for this value. Use \p UndefAllowed to exclude
 
  /// non-singleton constant ranges that may also be undef. Note that this
 
  /// function also returns a range if the range may include undef, but only
 
  /// contains a single element. In that case, it can be replaced by a constant.
 
  const ConstantRange &getConstantRange(bool UndefAllowed = true) const {
 
    assert(isConstantRange(UndefAllowed) &&
 
           "Cannot get the constant-range of a non-constant-range!");
 
    return Range;
 
  }
 
 
 
  std::optional<APInt> asConstantInteger() const {
 
    if (isConstant() && isa<ConstantInt>(getConstant())) {
 
      return cast<ConstantInt>(getConstant())->getValue();
 
    } else if (isConstantRange() && getConstantRange().isSingleElement()) {
 
      return *getConstantRange().getSingleElement();
 
    }
 
    return std::nullopt;
 
  }
 
 
 
  bool markOverdefined() {
 
    if (isOverdefined())
 
      return false;
 
    destroy();
 
    Tag = overdefined;
 
    return true;
 
  }
 
 
 
  bool markUndef() {
 
    if (isUndef())
 
      return false;
 
 
 
    assert(isUnknown());
 
    Tag = undef;
 
    return true;
 
  }
 
 
 
  bool markConstant(Constant *V, bool MayIncludeUndef = false) {
 
    if (isa<UndefValue>(V))
 
      return markUndef();
 
 
 
    if (isConstant()) {
 
      assert(getConstant() == V && "Marking constant with different value");
 
      return false;
 
    }
 
 
 
    if (ConstantInt *CI = dyn_cast<ConstantInt>(V))
 
      return markConstantRange(
 
          ConstantRange(CI->getValue()),
 
          MergeOptions().setMayIncludeUndef(MayIncludeUndef));
 
 
 
    assert(isUnknown() || isUndef());
 
    Tag = constant;
 
    ConstVal = V;
 
    return true;
 
  }
 
 
 
  bool markNotConstant(Constant *V) {
 
    assert(V && "Marking constant with NULL");
 
    if (ConstantInt *CI = dyn_cast<ConstantInt>(V))
 
      return markConstantRange(
 
          ConstantRange(CI->getValue() + 1, CI->getValue()));
 
 
 
    if (isa<UndefValue>(V))
 
      return false;
 
 
 
    if (isNotConstant()) {
 
      assert(getNotConstant() == V && "Marking !constant with different value");
 
      return false;
 
    }
 
 
 
    assert(isUnknown());
 
    Tag = notconstant;
 
    ConstVal = V;
 
    return true;
 
  }
 
 
 
  /// Mark the object as constant range with \p NewR. If the object is already a
 
  /// constant range, nothing changes if the existing range is equal to \p
 
  /// NewR and the tag. Otherwise \p NewR must be a superset of the existing
 
  /// range or the object must be undef. The tag is set to
 
  /// constant_range_including_undef if either the existing value or the new
 
  /// range may include undef.
 
  bool markConstantRange(ConstantRange NewR,
 
                         MergeOptions Opts = MergeOptions()) {
 
    assert(!NewR.isEmptySet() && "should only be called for non-empty sets");
 
 
 
    if (NewR.isFullSet())
 
      return markOverdefined();
 
 
 
    ValueLatticeElementTy OldTag = Tag;
 
    ValueLatticeElementTy NewTag =
 
        (isUndef() || isConstantRangeIncludingUndef() || Opts.MayIncludeUndef)
 
            ? constantrange_including_undef
 
            : constantrange;
 
    if (isConstantRange()) {
 
      Tag = NewTag;
 
      if (getConstantRange() == NewR)
 
        return Tag != OldTag;
 
 
 
      // Simple form of widening. If a range is extended multiple times, go to
 
      // overdefined.
 
      if (Opts.CheckWiden && ++NumRangeExtensions > Opts.MaxWidenSteps)
 
        return markOverdefined();
 
 
 
      assert(NewR.contains(getConstantRange()) &&
 
             "Existing range must be a subset of NewR");
 
      Range = std::move(NewR);
 
      return true;
 
    }
 
 
 
    assert(isUnknown() || isUndef());
 
 
 
    NumRangeExtensions = 0;
 
    Tag = NewTag;
 
    new (&Range) ConstantRange(std::move(NewR));
 
    return true;
 
  }
 
 
 
  /// Updates this object to approximate both this object and RHS. Returns
 
  /// true if this object has been changed.
 
  bool mergeIn(const ValueLatticeElement &RHS,
 
               MergeOptions Opts = MergeOptions()) {
 
    if (RHS.isUnknown() || isOverdefined())
 
      return false;
 
    if (RHS.isOverdefined()) {
 
      markOverdefined();
 
      return true;
 
    }
 
 
 
    if (isUndef()) {
 
      assert(!RHS.isUnknown());
 
      if (RHS.isUndef())
 
        return false;
 
      if (RHS.isConstant())
 
        return markConstant(RHS.getConstant(), true);
 
      if (RHS.isConstantRange())
 
        return markConstantRange(RHS.getConstantRange(true),
 
                                 Opts.setMayIncludeUndef());
 
      return markOverdefined();
 
    }
 
 
 
    if (isUnknown()) {
 
      assert(!RHS.isUnknown() && "Unknow RHS should be handled earlier");
 
      *this = RHS;
 
      return true;
 
    }
 
 
 
    if (isConstant()) {
 
      if (RHS.isConstant() && getConstant() == RHS.getConstant())
 
        return false;
 
      if (RHS.isUndef())
 
        return false;
 
      markOverdefined();
 
      return true;
 
    }
 
 
 
    if (isNotConstant()) {
 
      if (RHS.isNotConstant() && getNotConstant() == RHS.getNotConstant())
 
        return false;
 
      markOverdefined();
 
      return true;
 
    }
 
 
 
    auto OldTag = Tag;
 
    assert(isConstantRange() && "New ValueLattice type?");
 
    if (RHS.isUndef()) {
 
      Tag = constantrange_including_undef;
 
      return OldTag != Tag;
 
    }
 
 
 
    if (!RHS.isConstantRange()) {
 
      // We can get here if we've encountered a constantexpr of integer type
 
      // and merge it with a constantrange.
 
      markOverdefined();
 
      return true;
 
    }
 
 
 
    ConstantRange NewR = getConstantRange().unionWith(RHS.getConstantRange());
 
    return markConstantRange(
 
        std::move(NewR),
 
        Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
 
  }
 
 
 
  // Compares this symbolic value with Other using Pred and returns either
 
  /// true, false or undef constants, or nullptr if the comparison cannot be
 
  /// evaluated.
 
  Constant *getCompare(CmpInst::Predicate Pred, Type *Ty,
 
                       const ValueLatticeElement &Other,
 
                       const DataLayout &DL) const;
 
 
 
  unsigned getNumRangeExtensions() const { return NumRangeExtensions; }
 
  void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; }
 
};
 
 
 
static_assert(sizeof(ValueLatticeElement) <= 40,
 
              "size of ValueLatticeElement changed unexpectedly");
 
 
 
raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val);
 
} // end namespace llvm
 
#endif