//===----------------------------------------------------------------------===//
//
// 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_CODEGEN_CFIINSTBUILDER_H
#define LLVM_CODEGEN_CFIINSTBUILDER_H

#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/MC/MCDwarf.h"

namespace llvm {

/// Helper class for creating CFI instructions and inserting them into MIR.
class CFIInstBuilder {
  MachineFunction &MF;
  MachineBasicBlock &MBB;
  MachineBasicBlock::iterator InsertPt;

  /// MIFlag to set on a MachineInstr. Typically, FrameSetup or FrameDestroy.
  MachineInstr::MIFlag MIFlag;

  /// Selects DWARF register numbering: debug or exception handling. Should be
  /// consistent with the choice of the ELF section (.debug_frame or .eh_frame)
  /// where CFI will be encoded.
  bool IsEH;

  // Cache frequently used variables.
  const TargetRegisterInfo &TRI;
  const MCInstrDesc &CFIID;
  const MIMetadata MIMD; // Default-initialized, no debug location desired.

public:
  CFIInstBuilder(MachineBasicBlock &MBB, MachineBasicBlock::iterator InsertPt,
                 MachineInstr::MIFlag MIFlag, bool IsEH = true)
      : MF(*MBB.getParent()), MBB(MBB), MIFlag(MIFlag), IsEH(IsEH),
        TRI(*MF.getSubtarget().getRegisterInfo()),
        CFIID(MF.getSubtarget().getInstrInfo()->get(
            TargetOpcode::CFI_INSTRUCTION)) {
    setInsertPoint(InsertPt);
  }

  CFIInstBuilder(MachineBasicBlock *MBB, MachineInstr::MIFlag MIFlag,
                 bool IsEH = true)
      : CFIInstBuilder(*MBB, MBB->end(), MIFlag, IsEH) {}

  void setInsertPoint(MachineBasicBlock::iterator IP) { InsertPt = IP; }

  void insertCFIInst(const MCCFIInstruction &CFIInst) const {
    BuildMI(MBB, InsertPt, MIMD, CFIID)
        .addCFIIndex(MF.addFrameInst(CFIInst))
        .setMIFlag(MIFlag);
  }

  void buildDefCFA(MCRegister Reg, int64_t Offset) const {
    insertCFIInst(MCCFIInstruction::cfiDefCfa(
        nullptr, TRI.getDwarfRegNum(Reg, IsEH), Offset));
  }

  void buildDefCFARegister(MCRegister Reg) const {
    insertCFIInst(MCCFIInstruction::createDefCfaRegister(
        nullptr, TRI.getDwarfRegNum(Reg, IsEH)));
  }

  void buildDefCFAOffset(int64_t Offset, MCSymbol *Label = nullptr) const {
    insertCFIInst(MCCFIInstruction::cfiDefCfaOffset(Label, Offset));
  }

  void buildAdjustCFAOffset(int64_t Adjustment) const {
    insertCFIInst(MCCFIInstruction::createAdjustCfaOffset(nullptr, Adjustment));
  }

  void buildOffset(MCRegister Reg, int64_t Offset) const {
    insertCFIInst(MCCFIInstruction::createOffset(
        nullptr, TRI.getDwarfRegNum(Reg, IsEH), Offset));
  }

  void buildNegateRAState() const {
    insertCFIInst(MCCFIInstruction::createNegateRAState(nullptr));
  }

  void buildNegateRAStateWithPC() const {
    insertCFIInst(MCCFIInstruction::createNegateRAStateWithPC(nullptr));
  }

  void buildRegister(MCRegister Reg1, MCRegister Reg2) const {
    insertCFIInst(MCCFIInstruction::createRegister(
        nullptr, TRI.getDwarfRegNum(Reg1, IsEH),
        TRI.getDwarfRegNum(Reg2, IsEH)));
  }

  void buildWindowSave() const {
    insertCFIInst(MCCFIInstruction::createWindowSave(nullptr));
  }

  void buildRestore(MCRegister Reg) const {
    insertCFIInst(MCCFIInstruction::createRestore(
        nullptr, TRI.getDwarfRegNum(Reg, IsEH)));
  }

  void buildUndefined(MCRegister Reg) const {
    insertCFIInst(MCCFIInstruction::createUndefined(
        nullptr, TRI.getDwarfRegNum(Reg, IsEH)));
  }

  void buildSameValue(MCRegister Reg) const {
    insertCFIInst(MCCFIInstruction::createSameValue(
        nullptr, TRI.getDwarfRegNum(Reg, IsEH)));
  }

  void buildEscape(StringRef Bytes, StringRef Comment = "") const {
    insertCFIInst(
        MCCFIInstruction::createEscape(nullptr, Bytes, SMLoc(), Comment));
  }
};

} // namespace llvm

#endif // LLVM_CODEGEN_CFIINSTBUILDER_H
