//===-- W65816TargetTransformInfo.h - W65816 TTI ----------------*- 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
//
//===----------------------------------------------------------------------===//
//
// Phase 5.2 of GAP_CLOSURE_PLAN.md: minimal TTI for the W65816 backend.
//
// Required by the LTO inliner: the LLVM stock cost model assumes a
// 32-bit-native, FPU-equipped target, so i32 ops cost 1 and float/
// double ops cost 1.  On the W65816 those ops are 4-5x and ~20x more
// expensive respectively (libcalls into softFloat / softDouble).
// Without TTI the inliner sees an inflated "savings from inlining" for
// any function that touches i32 or float -- aggressive inlining bloats
// the final binary disproportionately.
//
// History:
//   Phase 1.4c added a full TTI with 4x i32 + 20x soft-float multipliers.
//   That surfaced a latent i32 termination-compare codegen bug at -O2
//   (smoke #77 bsearch hang).  TTI was reverted in Phase 1.4c follow-up.
//
//   Phase 5.2 reintroduces TTI with VERY MILD multipliers (2x i32, 5x
//   float) so the inliner is influenced enough to discourage gratuitous
//   i32/float inlining but not so much that aggressive cost-driven
//   transforms expose the bsearch bug.
//
//   2026-06-02: SepRepCleanup PHP-wrap-hoist fix landed (see
//   kMildCostModelEnabled comment below).  TTI multipliers are now ON
//   by default — the underlying i32 termination-compare miscompile was
//   the wrap-hoist using in-wrap (BUMPED) slot numbers for the gap
//   conflict-check.  Fix tracks effective post-unbump slots, so the
//   conflict check now bails when an outside-wrap LDA at slot N would
//   collide with the un-bumped in-wrap STA targeting slot N+1.
//
// If the bsearch smoke regresses, set kMildCostModelEnabled to false in
// this header and rebuild -- the TTI degrades to BasicTTIImpl defaults
// (LLVM stock costs) which we've shipped against for over a year.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_W65816_W65816TARGETTRANSFORMINFO_H
#define LLVM_LIB_TARGET_W65816_W65816TARGETTRANSFORMINFO_H

#include "W65816ISelLowering.h"
#include "W65816Subtarget.h"
#include "W65816TargetMachine.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/CodeGen/BasicTTIImpl.h"

namespace llvm {

class W65816TTIImpl final : public BasicTTIImplBase<W65816TTIImpl> {
  using BaseT = BasicTTIImplBase<W65816TTIImpl>;
  using TTI = TargetTransformInfo;
  friend BaseT;

  const W65816Subtarget *ST;
  const W65816TargetLowering *TLI;

  const W65816Subtarget *getST() const { return ST; }
  const W65816TargetLowering *getTLI() const { return TLI; }

  // Mild cost-model multipliers.  See class header comment for rationale.
  // The multipliers are intentionally conservative so the inliner
  // does NOT trigger the Phase 1.4c bsearch bug.
  static constexpr unsigned kI32CostMultiplier = 2;
  static constexpr unsigned kFloatCostMultiplier = 5;

  // Compile-time disable for the multipliers.  If a regression
  // surfaces, set to false and rebuild clang -- no other code changes
  // needed; TTI degrades to BasicTTIImpl defaults.
  //
  // STATUS (Phase 5.2 ship): kMildCostModelEnabled = true.  Initial
  // Phase 1.4c ship surfaced the smoke #77 bsearch hang because the
  // less-aggressive inliner kept bsearch out-of-line, which exposed
  // a latent SepRepCleanup PHP-wrap-hoist bug: an outside-wrap LDA
  // at slot N and an in-wrap STA at slot N+1 (which un-bumps to N
  // on hoist) reference the SAME memory address, but the
  // pre-hoist conflict check compared the LDA slot against the
  // BUMPED in-wrap WriteSlots set instead of the post-unbump
  // effective set, missing the collision.  Fix: track effective
  // (post-unbump) slot numbers in the SepRepCleanup PHP-wrap-hoist
  // ReadSlots/WriteSlots so the hoist gap-walk correctly bails on
  // outside-wrap LDA-vs-in-wrap-STA collisions.  With the fix,
  // PHP/PLP wrap stays intact in the bsearch termination compare,
  // the flag survives the PHI copies, and BNE branches on the
  // correct `lo < hi` condition.
  static constexpr bool kMildCostModelEnabled = true;

public:
  explicit W65816TTIImpl(const W65816TargetMachine *TM, const Function &F)
      : BaseT(TM, F.getDataLayout()),
        ST(TM->getSubtargetImpl(F)),
        TLI(ST->getTargetLowering()) {}

  /// \name Scalar TTI Implementations
  /// @{

  // Cost model for arithmetic.  We override this only to scale up i32
  // and float ops; everything else falls through to BasicTTIImpl.
  InstructionCost getArithmeticInstrCost(
      unsigned Opcode, Type *Ty, TTI::TargetCostKind CostKind,
      TTI::OperandValueInfo Op1Info = {TTI::OK_AnyValue, TTI::OP_None},
      TTI::OperandValueInfo Op2Info = {TTI::OK_AnyValue, TTI::OP_None},
      ArrayRef<const Value *> Args = {},
      const Instruction *CxtI = nullptr) const override {
    InstructionCost Base = BaseT::getArithmeticInstrCost(
        Opcode, Ty, CostKind, Op1Info, Op2Info, Args, CxtI);
    if (!kMildCostModelEnabled || !Base.isValid()) {
      return Base;
    }
    if (Ty && Ty->isFloatingPointTy()) {
      return Base * kFloatCostMultiplier;
    }
    // Scale i32+ integer ops; smaller types are native on a 16-bit
    // register target.
    if (Ty && Ty->isIntegerTy() && Ty->getScalarSizeInBits() >= 32) {
      return Base * kI32CostMultiplier;
    }
    return Base;
  }

  /// @}
};

} // end namespace llvm

#endif // LLVM_LIB_TARGET_W65816_W65816TARGETTRANSFORMINFO_H
