//===-- LVLocation.h --------------------------------------------*- 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 LVOperation and LVLocation classes, which are used
// to describe variable locations.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H
#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H

#include "llvm/DebugInfo/LogicalView/Core/LVObject.h"

namespace llvm {
namespace logicalview {

using LVLineRange = std::pair<LVLine *, LVLine *>;

// The DW_AT_data_member_location attribute is a simple member offset.
const LVSmall LVLocationMemberOffset = 0;

class LVOperation final {
  // To describe an operation:
  // OpCode
  // Operands[0]: First operand.
  // Operands[1]: Second operand.
  //   OP_bregx, OP_bit_piece, OP_[GNU_]const_type,
  //   OP_[GNU_]deref_type, OP_[GNU_]entry_value, OP_implicit_value,
  //   OP_[GNU_]implicit_pointer, OP_[GNU_]regval_type, OP_xderef_type.
  LVSmall Opcode = 0;
  uint64_t Operands[2];

public:
  LVOperation() = delete;
  LVOperation(LVSmall Opcode, LVUnsigned Operand1, LVUnsigned Operand2)
      : Opcode(Opcode) {
    Operands[0] = Operand1;
    Operands[1] = Operand2;
  }
  LVOperation(const LVOperation &) = delete;
  LVOperation &operator=(const LVOperation &) = delete;
  ~LVOperation() = default;

  LVSmall getOpcode() const { return Opcode; }
  uint64_t getOperand1() const { return Operands[0]; }
  uint64_t getOperand2() const { return Operands[1]; }
  std::string getOperandsDWARFInfo();
  std::string getOperandsCodeViewInfo();

  void print(raw_ostream &OS, bool Full = true) const;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  void dump() { print(dbgs()); }
#endif
};

class LVLocation : public LVObject {
  enum class Property {
    IsAddressRange,
    IsBaseClassOffset,
    IsBaseClassStep,
    IsClassOffset,
    IsFixedAddress,
    IsLocationSimple,
    IsGapEntry,
    IsOperation,
    IsOperationList,
    IsRegister,
    IsStackOffset,
    IsDiscardedRange,
    IsInvalidRange,
    IsInvalidLower,
    IsInvalidUpper,
    IsCallSite,
    LastEntry
  };
  // Typed bitvector with properties for this location.
  LVProperties<Property> Properties;

  // True if the location it is associated with a debug range.
  bool hasAssociatedRange() const {
    return !getIsClassOffset() && !getIsDiscardedRange();
  }

protected:
  // Line numbers associated with locations ranges.
  LVLine *LowerLine = nullptr;
  LVLine *UpperLine = nullptr;

  // Active range:
  // LowPC: an offset from an applicable base address, not a PC value.
  // HighPC: an offset from an applicable base address, or a length.
  LVAddress LowPC = 0;
  LVAddress HighPC = 0;

  void setKind();

public:
  LVLocation() : LVObject() { setIsLocation(); }
  LVLocation(const LVLocation &) = delete;
  LVLocation &operator=(const LVLocation &) = delete;
  virtual ~LVLocation() = default;

  PROPERTY(Property, IsAddressRange);
  PROPERTY(Property, IsBaseClassOffset);
  PROPERTY(Property, IsBaseClassStep);
  PROPERTY_1(Property, IsClassOffset, IsLocationSimple);
  PROPERTY_1(Property, IsFixedAddress, IsLocationSimple);
  PROPERTY(Property, IsLocationSimple);
  PROPERTY(Property, IsGapEntry);
  PROPERTY(Property, IsOperationList);
  PROPERTY(Property, IsOperation);
  PROPERTY(Property, IsRegister);
  PROPERTY_1(Property, IsStackOffset, IsLocationSimple);
  PROPERTY(Property, IsDiscardedRange);
  PROPERTY(Property, IsInvalidRange);
  PROPERTY(Property, IsInvalidLower);
  PROPERTY(Property, IsInvalidUpper);
  PROPERTY(Property, IsCallSite);

  const char *kind() const override;
  // Mark the locations that have only DW_OP_fbreg as stack offset based.
  virtual void updateKind() {}

  // Line numbers for locations.
  const LVLine *getLowerLine() const { return LowerLine; }
  void setLowerLine(LVLine *Line) { LowerLine = Line; }
  const LVLine *getUpperLine() const { return UpperLine; }
  void setUpperLine(LVLine *Line) { UpperLine = Line; }

  // Addresses for locations.
  LVAddress getLowerAddress() const override { return LowPC; }
  void setLowerAddress(LVAddress Address) override { LowPC = Address; }
  LVAddress getUpperAddress() const override { return HighPC; }
  void setUpperAddress(LVAddress Address) override { HighPC = Address; }

  std::string getIntervalInfo() const;

  bool validateRanges();

  // In order to calculate a symbol coverage (percentage), take the ranges
  // and obtain the number of units (bytes) covered by those ranges. We can't
  // use the line numbers, because they can be zero or invalid.
  // We return:
  //   false: No locations or multiple locations.
  //   true: a single location.
  static bool calculateCoverage(LVLocations *Locations, unsigned &Factor,
                                float &Percentage);

  virtual void addObject(LVAddress LowPC, LVAddress HighPC,
                         LVUnsigned SectionOffset, uint64_t LocDescOffset) {}
  virtual void addObject(LVSmall Opcode, LVUnsigned Operand1,
                         LVUnsigned Operand2) {}

  static void print(LVLocations *Locations, raw_ostream &OS, bool Full = true);
  void printInterval(raw_ostream &OS, bool Full = true) const;
  void printRaw(raw_ostream &OS, bool Full = true) const;
  virtual void printRawExtra(raw_ostream &OS, bool Full = true) const {}

  void print(raw_ostream &OS, bool Full = true) const override;
  void printExtra(raw_ostream &OS, bool Full = true) const override;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  void dump() const override { print(dbgs()); }
#endif
};

class LVLocationSymbol final : public LVLocation {
  // Location descriptors for the active range.
  LVAutoOperations *Entries = nullptr;

  void updateKind() override;

public:
  LVLocationSymbol() : LVLocation() {}
  LVLocationSymbol(const LVLocationSymbol &) = delete;
  LVLocationSymbol &operator=(const LVLocationSymbol &) = delete;
  ~LVLocationSymbol() { delete Entries; };

  void addObject(LVAddress LowPC, LVAddress HighPC, LVUnsigned SectionOffset,
                 uint64_t LocDescOffset) override;
  void addObject(LVSmall Opcode, LVUnsigned Operand1,
                 LVUnsigned Operand2) override;

  void printRawExtra(raw_ostream &OS, bool Full = true) const override;
  void printExtra(raw_ostream &OS, bool Full = true) const override;
};

} // end namespace logicalview
} // end namespace llvm

#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H
