//===-- llvm/GEPNoWrapFlags.h - NoWrap flags for GEPs -----------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the nowrap flags for getelementptr operators.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_IR_GEPNOWRAPFLAGS_H
#define LLVM_IR_GEPNOWRAPFLAGS_H

#include <assert.h>

namespace llvm {

/// Represents flags for the getelementptr instruction/expression.
/// The following flags are supported:
///  * inbounds (implies nusw)
///  * nusw (no unsigned signed wrap)
///  * nuw (no unsigned wrap)
/// See LangRef for a description of their semantics.
class GEPNoWrapFlags {
  enum : unsigned {
    InBoundsFlag = (1 << 0),
    NUSWFlag = (1 << 1),
    NUWFlag = (1 << 2),
  };

  unsigned Flags;
  GEPNoWrapFlags(unsigned Flags) : Flags(Flags) {
    assert((!isInBounds() || hasNoUnsignedSignedWrap()) &&
           "inbounds implies nusw");
  }

public:
  GEPNoWrapFlags() : Flags(0) {}
  // For historical reasons, interpret plain boolean as InBounds.
  // TODO: Migrate users to pass explicit GEPNoWrapFlags and remove this ctor.
  GEPNoWrapFlags(bool IsInBounds)
      : Flags(IsInBounds ? (InBoundsFlag | NUSWFlag) : 0) {}

  static GEPNoWrapFlags none() { return GEPNoWrapFlags(); }
  static GEPNoWrapFlags all() {
    return GEPNoWrapFlags(InBoundsFlag | NUSWFlag | NUWFlag);
  }
  static GEPNoWrapFlags inBounds() {
    return GEPNoWrapFlags(InBoundsFlag | NUSWFlag);
  }
  static GEPNoWrapFlags noUnsignedSignedWrap() {
    return GEPNoWrapFlags(NUSWFlag);
  }
  static GEPNoWrapFlags noUnsignedWrap() { return GEPNoWrapFlags(NUWFlag); }

  static GEPNoWrapFlags fromRaw(unsigned Flags) {
    return GEPNoWrapFlags(Flags);
  }
  unsigned getRaw() const { return Flags; }

  bool isInBounds() const { return Flags & InBoundsFlag; }
  bool hasNoUnsignedSignedWrap() const { return Flags & NUSWFlag; }
  bool hasNoUnsignedWrap() const { return Flags & NUWFlag; }

  GEPNoWrapFlags withoutInBounds() const {
    return GEPNoWrapFlags(Flags & ~InBoundsFlag);
  }
  GEPNoWrapFlags withoutNoUnsignedSignedWrap() const {
    return GEPNoWrapFlags(Flags & ~(InBoundsFlag | NUSWFlag));
  }
  GEPNoWrapFlags withoutNoUnsignedWrap() const {
    return GEPNoWrapFlags(Flags & ~NUWFlag);
  }

  /// Given (gep (gep p, x), y), determine the nowrap flags for (gep p, x+y).
  GEPNoWrapFlags intersectForOffsetAdd(GEPNoWrapFlags Other) const {
    GEPNoWrapFlags Res = *this & Other;
    // Without inbounds, we could only preserve nusw if we know that x + y does
    // not wrap.
    if (!Res.isInBounds() && Res.hasNoUnsignedSignedWrap())
      Res = Res.withoutNoUnsignedSignedWrap();
    return Res;
  }

  /// Given (gep (gep p, x), y), determine the nowrap flags for
  /// (gep (gep, p, y), x).
  GEPNoWrapFlags intersectForReassociate(GEPNoWrapFlags Other) const {
    GEPNoWrapFlags Res = *this & Other;
    // We can only preserve inbounds and nusw if nuw is also set.
    if (!Res.hasNoUnsignedWrap())
      return none();
    return Res;
  }

  bool operator==(GEPNoWrapFlags Other) const { return Flags == Other.Flags; }
  bool operator!=(GEPNoWrapFlags Other) const { return !(*this == Other); }

  GEPNoWrapFlags operator&(GEPNoWrapFlags Other) const {
    return GEPNoWrapFlags(Flags & Other.Flags);
  }
  GEPNoWrapFlags operator|(GEPNoWrapFlags Other) const {
    return GEPNoWrapFlags(Flags | Other.Flags);
  }
  GEPNoWrapFlags &operator&=(GEPNoWrapFlags Other) {
    Flags &= Other.Flags;
    return *this;
  }
  GEPNoWrapFlags &operator|=(GEPNoWrapFlags Other) {
    Flags |= Other.Flags;
    return *this;
  }
};

} // end namespace llvm

#endif // LLVM_IR_GEPNOWRAPFLAGS_H
