//===-- W65816InstrInfo.h - W65816 Instruction Information ------*- 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 the W65816 implementation of the TargetInstrInfo class.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_W65816_W65816INSTRINFO_H
#define LLVM_LIB_TARGET_W65816_W65816INSTRINFO_H

#include "W65816RegisterInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"

#define GET_INSTRINFO_HEADER
#include "W65816GenInstrInfo.inc"

namespace llvm {

class W65816Subtarget;

// Shared codegen helpers used across multiple W65816 passes.  Defined in
// W65816InstrInfo.cpp so all passes link against a single source-of-truth.
namespace W65816Helpers {

// Map a conditional Bxx opcode (BEQ/BNE/BCS/BCC/BMI/BPL/BVS/BVC) to its
// inverse condition.  Returns 0 if not a recognised conditional branch.
unsigned invertCondOpcode(unsigned Opc);

// Map a *_StackRel MC opcode (LDA/STA/ADC/SBC/CMP/AND/ORA/EOR) to its
// DP-immediate counterpart (LDA_DP, STA_DP, ...).  Returns 0 if the
// opcode isn't one of the eight stack-rel MC ops.
unsigned getDpOpcodeForStackRel(unsigned Opc);

// True when Opc is one of the eight stack-rel MC ops above.  Defined in
// terms of getDpOpcodeForStackRel so the two helpers can't drift apart.
inline bool isStackRelOpcode(unsigned Opc) {
  return getDpOpcodeForStackRel(Opc) != 0;
}

// Map a physical IMG register (IMG0..IMG15) to its DP address.  IMG0..7
// live at $D0..$DE (caller-save); IMG8..15 live at $C0..$CE (callee-save
// per W65816ImgCalleeSave).  Returns -1 if Reg isn't an IMG.
int imgDPAddr(unsigned Reg);

// Allowlist of tied-def Acc16 consumer pseudos: instructions that take
// an Acc16 source operand which is tied to the same-named Acc16 def.
// Shared between W65816TiedDefSpill (stack-route bridge) and
// W65816ABridgeViaX (X/Y-route bridge); both passes target the same
// consumers so they must observe the same set.
bool isTiedAcc16Consumer(unsigned Opc);

// True when MI is a tied-def Acc16 consumer AND at least one of its
// operands is tied to a def.  Wraps isTiedAcc16Consumer with the
// per-MI operand check the bridge passes perform on every candidate.
bool hasTiedAcc16Src(const MachineInstr &MI);

} // namespace W65816Helpers

class W65816InstrInfo : public W65816GenInstrInfo {
  const W65816RegisterInfo RI;
  virtual void anchor();

public:
  explicit W65816InstrInfo(const W65816Subtarget &STI);

  const W65816RegisterInfo &getRegisterInfo() const { return RI; }

  void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
                   const DebugLoc &DL, Register DestReg, Register SrcReg,
                   bool KillSrc, bool RenamableDest = false,
                   bool RenamableSrc = false) const override;

  void storeRegToStackSlot(
      MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg,
      bool isKill, int FrameIndex, const TargetRegisterClass *RC, Register VReg,
      MachineInstr::MIFlag Flags = MachineInstr::NoFlags) const override;
  void loadRegFromStackSlot(
      MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register DestReg,
      int FrameIdx, const TargetRegisterClass *RC, Register VReg,
      unsigned SubReg = 0,
      MachineInstr::MIFlag Flags = MachineInstr::NoFlags) const override;

  // Override the default rematerializability check to recognise LDAfi
  // from a *fixed* (immutable) frame index — i.e. an arg slot — as
  // trivially rematerializable.  Without this, the greedy allocator
  // spills arg loads to a fresh local slot the moment A is needed for
  // anything else, then reloads from the local slot at every use.
  // With it, the allocator just re-emits `LDA arg_slot,S` at each use
  // and the `STA local; LDA local; LDA local` cluster collapses to a
  // single `LDA arg_slot,S`.  Spill-slot LDAfi (regular FI) is *not*
  // rematerializable — that loads a computed value.
  bool isReMaterializableImpl(const MachineInstr &MI) const override;

  // Post-RA expansion of STB_DPIndLongY (atomic i8-store-through-[dp],Y)
  // into SEP #$20 ; sta [dp],y ; REP #$20.  Kept as one pseudo through
  // register allocation so the -O0 fast allocator cannot insert a 16-bit
  // reload inside the M=8 window; expanded here (post-RA, before
  // SepRepCleanup and the AsmPrinter) into real MIs so both the SEP/REP
  // coalescer and the AsmPrinter's mode-tracking see normal instructions.
  bool expandPostRAPseudo(MachineInstr &MI) const override;

  // Tell the framework which pseudos are direct stack-slot loads/stores.
  // MachineCSE, machine-licm, and peephole-opt use these hooks to elide
  // redundant store/load pairs and to hoist invariants.  Without them,
  // patterns like `STAfi A, slot; LDAfi slot, A` (introduced by the
  // greedy allocator's COPY-of-physreg expansion) survive into final
  // asm as `sta x,s; lda x,s` no-op pairs.
  Register isLoadFromStackSlot(const MachineInstr &MI,
                               int &FrameIndex) const override;
  Register isStoreToStackSlot(const MachineInstr &MI,
                              int &FrameIndex) const override;

  // Byte-accurate size of an instruction (or an upper bound for
  // pseudos that AsmPrinter expands to multiple MC instructions).
  // Used by W65816BranchExpand to compute branch distances precisely
  // enough to decide when to lengthen a conditional branch.  Real
  // instructions with a Size set in tablegen get that value;
  // pseudos that emit nothing (PHI, COPY, ADJCALLSTACKDOWN/UP,
  // KILL, IMPLICIT_DEF, REG_SEQUENCE, BUNDLE, etc.) report 0 bytes;
  // codegen pseudos with Size==0 in tablegen but a non-trivial
  // AsmPrinter expansion get an upper-bound estimate.
  unsigned getInstSizeInBytes(const MachineInstr &MI) const override;

  // PEI uses this to track the running SP shift inside a call
  // sequence and pass it to eliminateFrameIndex as SPAdj.  Our
  // ADJCALLSTACKDOWN does NOT physically shift SP — the PUSH16/PUSH16X
  // pseudos do that incrementally as args get pushed.  Override the
  // default so PEI knows: ADJCALLSTACKDOWN/UP contribute 0 (no SP
  // shift), PUSH16/PUSH16X contribute +2 each (one byte-pair pushed).
  // Without this override, PEI applies the full ADJCALLSTACKDOWN
  // amount as SPAdj at the very *start* of the call sequence,
  // producing FI offsets that pretend SP has already shifted — and
  // any STAfi/LDAfi to a *local* before the actual PUSH16 happens
  // ends up writing past the locals into the caller's stack
  // (corrupting the return address, observed for `int eval(int a,
  // int b, int c) { return a*b + c; }` under fast regalloc).
  int getSPAdjust(const MachineInstr &MI) const override;

  // Branch-control hooks.  These now decode our real branch opcodes
  // (BEQ/BNE/BCS/BCC/BMI/BPL/BVS/BVC and BRA/BRL/JMP_Abs) so
  // BranchFolder and MachineBlockPlacement can rearrange blocks.
  // Cond is encoded as a single Imm operand holding the conditional
  // branch's opcode; reverseBranchCondition flips it via opcode map.
  // JMP_AbsInd / JML_Long return "unanalyzable" — they're indirect or
  // bank-crossing, which the layout passes can't reason about.
  bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
                     MachineBasicBlock *&FBB,
                     SmallVectorImpl<MachineOperand> &Cond,
                     bool AllowModify) const override;
  unsigned removeBranch(MachineBasicBlock &MBB,
                        int *BytesRemoved = nullptr) const override;
  unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB,
                        MachineBasicBlock *FBB,
                        ArrayRef<MachineOperand> Cond, const DebugLoc &DL,
                        int *BytesAdded = nullptr) const override;
  bool reverseBranchCondition(
      SmallVectorImpl<MachineOperand> &Cond) const override;
  MachineBasicBlock *getBranchDestBlock(
      const MachineInstr &MI) const override;
};

} // namespace llvm

#endif
