//===-- W65816ISelLowering.h - W65816 DAG Lowering Interface ----*- 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 interfaces that W65816 uses to lower LLVM code into a
// selection DAG.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_W65816_W65816ISELLOWERING_H
#define LLVM_LIB_TARGET_W65816_W65816ISELLOWERING_H

#include "W65816.h"
#include "llvm/CodeGen/TargetLowering.h"

// W65816ISD::{Wrapper, CALL, RET_GLUE, ...} are defined by TableGen in
// W65816GenSDNodeInfo.inc; that header is included transitively via
// W65816SelectionDAGInfo.h.

namespace llvm {

class W65816Subtarget;

class W65816TargetLowering : public TargetLowering {
public:
  explicit W65816TargetLowering(const TargetMachine &TM,
                                const W65816Subtarget &STI);

  SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
                               bool IsVarArg,
                               const SmallVectorImpl<ISD::InputArg> &Ins,
                               const SDLoc &DL, SelectionDAG &DAG,
                               SmallVectorImpl<SDValue> &InVals) const override;

  SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI,
                    SmallVectorImpl<SDValue> &InVals) const override;

  SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
                      const SmallVectorImpl<ISD::OutputArg> &Outs,
                      const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
                      SelectionDAG &DAG) const override;

  SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;

  // Lock i16 shift amounts to i16 (not i32) even when i32 is a legal
  // type.  Without this, the DAG combiner promotes i16 shift amounts
  // to i32 once i32 is registered as legal, leaving (sra i16, i32:K)
  // with no matching pattern.  Only narrow when LHS is i16; leave i32
  // shifts (which go to libcall via LowerShift) alone.
  MVT getScalarShiftAmountTy(const DataLayout &DL,
                             EVT LHSTy) const override {
    if (LHSTy == MVT::i16 || LHSTy == MVT::i8) return MVT::i16;
    return TargetLoweringBase::getScalarShiftAmountTy(DL, LHSTy);
  }

  // ptr32-mode hook: with patches/0007-targetlowering-virtual-
  // gettypeconversion making the base function virtual, this can be
  // overridden to force i64 to expand directly to i16 halves rather
  // than going through i32 (the next-smaller-legal type).  Currently
  // not overridden — the override-calling-base passthrough caused
  // regressions in unrelated functions (likely due to subtle
  // de-virtualization changes when the function becomes virtual).
  // Future fix needs to test the override more carefully.

  MachineBasicBlock *
  EmitInstrWithCustomInserter(MachineInstr &MI,
                              MachineBasicBlock *MBB) const override;

  // The 65816 has no alignment requirement on memory access — any
  // address is fine.  Telling LLVM this lets it emit single 16-bit
  // loads/stores even when the IR alignment is 1, instead of
  // synthesising the value from two byte loads + shl-or.
  bool allowsMisalignedMemoryAccesses(EVT VT, unsigned AddrSpace = 0,
                                      Align Alignment = Align(1),
                                      MachineMemOperand::Flags Flags =
                                          MachineMemOperand::MONone,
                                      unsigned *Fast = nullptr) const override {
    if (Fast) *Fast = 1;
    return true;
  }

  // Disable LLVM's magic-constant expansion of sdiv/srem by power-of-2.
  // The default expansion generates BUILD_VECTOR (used as a "splat shifter"
  // intermediate) which we can't lower; without an override, every sdiv/srem
  // by a pow2 constant crashes ISel.  Returning the original node leaves it
  // intact for the libcall lowering path (SDIV/SREM are LibCall in our
  // ctor — see setOperationAction calls above).
  SDValue BuildSDIVPow2(SDNode *N, const APInt &Divisor,
                        SelectionDAG &DAG,
                        SmallVectorImpl<SDNode *> &Created) const override {
    return SDValue(N, 0);
  }
  SDValue BuildSREMPow2(SDNode *N, const APInt &Divisor,
                        SelectionDAG &DAG,
                        SmallVectorImpl<SDNode *> &Created) const override {
    return SDValue(N, 0);
  }

  SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override;

  // Inline-asm register constraints.  Supports:
  //   "a" / "{a}"  — accumulator (A) — Acc16 (or Acc8 for i8 type)
  //   "x" / "{x}"  — index X — Idx16 (or Idx8)
  //   "y" / "{y}"  — index Y — Idx16 (or Idx8)
  //   "r"          — any allocatable register — Acc16 by default
  // Letting users name A/X/Y opens up direct toolbox-call sequences,
  // hand-written math kernels, and any other place where the back-end
  // doesn't already know to use a particular reg.
  std::pair<unsigned, const TargetRegisterClass *>
  getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
                               StringRef Constraint,
                               MVT VT) const override;

  // Classify single-letter constraints 'a','x','y' as register-class
  // constraints so SelectionDAGBuilder routes them to the resolver
  // above rather than reporting "unknown asm constraint."
  ConstraintType getConstraintType(StringRef Constraint) const override {
    if (Constraint.size() == 1) {
      switch (Constraint[0]) {
        case 'a': case 'x': case 'y': case 'r':
          return C_RegisterClass;
        default: break;
      }
    }
    return TargetLowering::getConstraintType(Constraint);
  }

  // Force i32 / i64 shifts through a libcall (__ashlsi3 / __lshrsi3 /
  // __ashrsi3) instead of LLVM's default ExpandToParts strategy, which
  // emits an SHL_PARTS node we have no pattern for.  ExpandToParts also
  // produces a long select-based sequence; the libcall is both smaller
  // and matches our existing libcall-based approach for i16 mul/div.
  ShiftLegalizationStrategy
  preferredShiftLegalizationStrategy(SelectionDAG &DAG, SDNode *N,
                                     unsigned ExpansionFactor) const override {
    if (N->getValueType(0).getSizeInBits() > 16)
      return ShiftLegalizationStrategy::LowerToLibcall;
    return TargetLowering::preferredShiftLegalizationStrategy(DAG, N,
                                                              ExpansionFactor);
  }

  // i16 MUL goes through __mulhi3 libcall.  Tell the DAG combiner that
  // decomposing a constant multiply into shifts and adds is profitable:
  // a libcall is ~12 instructions, while `(mul x, 3)` -> `(add x, (shl
  // x, 1))` is 5.  i32 stays libcall — the per-half shift+add+chain
  // expansion comes out larger than the __mulsi3 call.
  bool decomposeMulByConstant(LLVMContext &Context, EVT VT,
                              SDValue C) const override {
    return VT == MVT::i16;
  }

  // The DAG combiner has a transform `(trunc (shl X, K)) -> (shl (trunc X), K)`
  // gated on `isTypeDesirableForOp(SHL, NarrowVT)`.  Our LowerShift expands
  // i8 SHL/SRL/SRA to `(trunc (shift (zext X), K))`; the combiner then
  // narrows it back to `(shift X, K)` of i8, which re-enters LowerShift —
  // an infinite loop that hangs `unsigned char x << 1` at -O1/-O2.
  // Return false for shifts on i8 to disable that narrowing combine and
  // keep the operation in i16 once we've widened it.
  bool isTypeDesirableForOp(unsigned Opc, EVT VT) const override {
    if (VT == MVT::i8 &&
        (Opc == ISD::SHL || Opc == ISD::SRL || Opc == ISD::SRA))
      return false;
    return TargetLowering::isTypeDesirableForOp(Opc, VT);
  }

  // Disallow merging stores into wider ones.  With ptr32 active and i32
  // a Custom-lowered op, the SDAG combiner's MergeConsecutiveStores
  // takes our LowerStore-split pair (2x i16 stores at &t and &t+2) and
  // merges them back into a single i32 store, which re-enters
  // LowerStore, splits again, and loops forever — observed as
  // "LLVM ERROR: out of memory" on `*t = K` for any K (including 0
  // when the SDAG state lets the combiner pick the merge ahead of any
  // STZ-pattern simplification).  Anything wider than i16 has no
  // legal ptr-store pattern in our backend anyway, so merging into
  // wider VTs is purely counterproductive.
  bool canMergeStoresTo(unsigned AS, EVT MemVT,
                        const MachineFunction &MF) const override {
    if (MemVT.isInteger() && MemVT.getSizeInBits() > 16)
      return false;
    return TargetLowering::canMergeStoresTo(AS, MemVT, MF);
  }

private:
  SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerBRIND(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerSignExtend(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerShift(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerDynamicStackalloc(SDValue Op, SelectionDAG &DAG) const;
  // Foundation hooks for ptr32 mode.  In ptr16 mode (current default),
  // both return SDValue() so the legalizer falls through to the default
  // i16-pointer LDAptr/STAptr selection.  When ptr32 mode is enabled
  // (PointerWidth=32 + Wide32 added as i32 reg class), they detect i32
  // addresses and wrap the load/store in W65816ISD::LD_PTR / ST_PTR /
  // STB_PTR so the [dp],Y inserter takes the bank byte from the
  // pointer's hi half instead of forcing 0.
  SDValue LowerLoad(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerStore(SDValue Op, SelectionDAG &DAG) const;
  // ZERO/SIGN/ANY_EXTEND i16 -> i32 and TRUNCATE i32 -> i16 lowering
  // via REG_SEQUENCE / EXTRACT_SUBREG on the sub_lo/sub_hi indexes of
  // the Wide32 register class.  Active once i32 is registered as a
  // legal type.
  SDValue LowerExtend(SDValue Op, SelectionDAG &DAG) const;
  SDValue LowerTruncate(SDValue Op, SelectionDAG &DAG) const;
  // SIGN_EXTEND_INREG i32 with inner type i1 / i8 / i16: sign-extend
  // the low N bits of the i32 input to fill all 32 bits.  Splits to
  // (sext_inreg lo, innerVT) for the low half and SRA #15 of the
  // resulting i16 for the high half.
  SDValue LowerSignExtendInReg(SDValue Op, SelectionDAG &DAG) const;
  // ADD/SUB/AND/OR/XOR i32 split into per-half i16 ops.  The carry-
  // chain ADDC/ADDE pseudos handle the cross-half link for ADD/SUB.
  SDValue LowerI32Bin(SDValue Op, SelectionDAG &DAG) const;
  // i32 ConstantNode: split into two i16 constants and REG_SEQUENCE.
  SDValue LowerI32Constant(SDValue Op, SelectionDAG &DAG) const;
  // i32 MUL: detect (zext i16 a) * (zext i16 b) — or operands with
  // provably-zero high 16 bits — and emit __umulhisi3 (16x16 -> 32)
  // instead of __mulsi3 (32x32 -> 32).  Cuts ~30% off the canonical
  // sumSquares-style loop.
  SDValue LowerMUL_I32(SDValue Op, SelectionDAG &DAG) const;
};

} // namespace llvm

#endif
