//===- bolt/Core/MCPlus.h - Helpers for MCPlus instructions -----*- 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 contains declarations for helper functions for adding annotations
// to MCInst objects.
//
//===----------------------------------------------------------------------===//

#ifndef BOLT_CORE_MCPLUS_H
#define BOLT_CORE_MCPLUS_H

#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/Support/Casting.h"
#include <vector>

namespace llvm {
namespace bolt {

// NOTE: using SmallVector for instruction list results in a memory regression.
using InstructionListType = std::vector<MCInst>;

namespace MCPlus {

/// This type represents C++ EH info for a callsite.  The symbol is the landing
/// pad and the uint64_t represents the action.
using MCLandingPad = std::pair<const MCSymbol *, uint64_t>;

/// An extension to MCInst is provided via an extra operand of type MCInst with
/// ANNOTATION_LABEL opcode (i.e. we are tying an annotation instruction to an
/// existing one). The annotation instruction contains a list of Immediate
/// operands. Each operand either contains a value, or is a pointer to
/// an instance of class MCAnnotation.
///
/// There are 2 distinct groups of annotations. The first group is a first-class
/// annotation that affects semantics of the instruction, such as an
/// exception-handling or jump table information. The second group contains
/// information that is supplement, and could be discarded without affecting
/// correctness of the program. Debugging information, and profile information
/// belong to the second group.
///
/// Note: some optimization/transformation passes could use generic annotations
///       inside the pass and remove these annotations after the pass. In this
///       case, the internal state saved with annotations could affect the
///       correctness.
///
/// For the first group, we use a reserved annotation index. Operands in
/// the first groups store a value of an annotation in the immediate field
/// of their corresponding operand.
///
/// Annotations in the second group could be addressed either by name, or by
/// by and index which could be queried by providing a name.
class MCAnnotation {
public:
  enum Kind {
    kEHLandingPad,        /// Exception handling landing pad.
    kEHAction,            /// Action for exception handler.
    kGnuArgsSize,         /// GNU args size.
    kJumpTable,           /// Jump Table.
    kTailCall,            /// Tail call.
    kConditionalTailCall, /// CTC.
    kOffset,              /// Offset in the function.
    kGeneric              /// First generic annotation.
  };

  virtual void print(raw_ostream &OS) const = 0;
  virtual bool equals(const MCAnnotation &) const = 0;
  virtual ~MCAnnotation() {}

protected:
  MCAnnotation() {}

private:
  // noncopyable
  MCAnnotation(const MCAnnotation &Other) = delete;
  MCAnnotation &operator=(const MCAnnotation &Other) = delete;
};

/// Instances of this class represent a simple annotation with a
/// specific value type.
/// Note that if ValueType contains any heap allocated memory, it will
/// only be freed if the annotation is removed with the
/// MCPlusBuilder::removeAnnotation method.  This is because all
/// annotations are arena allocated.
template <typename ValueType> class MCSimpleAnnotation : public MCAnnotation {
public:
  ValueType &getValue() { return Value; }
  bool equals(const MCAnnotation &Other) const override {
    return Value == static_cast<const MCSimpleAnnotation &>(Other).Value;
  }
  explicit MCSimpleAnnotation(const ValueType &Val) : Value(Val) {}

  void print(raw_ostream &OS) const override { OS << Value; }

private:
  ValueType Value;
};

/// Return a number of operands in \Inst excluding operands representing
/// annotations.
inline unsigned getNumPrimeOperands(const MCInst &Inst) {
  if (Inst.getNumOperands() > 0 && std::prev(Inst.end())->isInst()) {
    assert(std::prev(Inst.end())->getInst()->getOpcode() ==
           TargetOpcode::ANNOTATION_LABEL);
    return Inst.getNumOperands() - 1;
  }
  return Inst.getNumOperands();
}

/// Return iterator range of operands excluding operands representing
/// annotations.
inline iterator_range<MCInst::iterator> primeOperands(MCInst &Inst) {
  return iterator_range<MCInst::iterator>(
      Inst.begin(), Inst.begin() + getNumPrimeOperands(Inst));
}

inline iterator_range<MCInst::const_iterator>
primeOperands(const MCInst &Inst) {
  return iterator_range<MCInst::const_iterator>(
      Inst.begin(), Inst.begin() + getNumPrimeOperands(Inst));
}

} // namespace MCPlus

} // namespace bolt
} // namespace llvm

#endif
