// Copyright 2026 The IREE Authors
//
// Licensed 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

#include "iree/compiler/Codegen/Utils/CodegenOptions.h"

IREE_DEFINE_COMPILER_OPTION_FLAGS(mlir::iree_compiler::CPUCodegenOptions);
IREE_DEFINE_COMPILER_OPTION_FLAGS(mlir::iree_compiler::GPUCodegenOptions);

// Out-of-line definitions for the `llvm::cl::parser` specializations declared
// in CodegenOptions.h.
namespace llvm::cl {

template class basic_parser<mlir::iree_compiler::CPUCodegenOptions>;
bool parser<mlir::iree_compiler::CPUCodegenOptions>::parse(
    Option &, StringRef, StringRef, mlir::iree_compiler::CPUCodegenOptions &) {
  return false;
}
void parser<mlir::iree_compiler::CPUCodegenOptions>::printOptionDiff(
    const Option &, mlir::iree_compiler::CPUCodegenOptions, const OptVal &,
    size_t) const {}
void parser<mlir::iree_compiler::CPUCodegenOptions>::anchor() {}

template class basic_parser<mlir::iree_compiler::GPUCodegenOptions>;
bool parser<mlir::iree_compiler::GPUCodegenOptions>::parse(
    Option &, StringRef, StringRef, mlir::iree_compiler::GPUCodegenOptions &) {
  return false;
}
void parser<mlir::iree_compiler::GPUCodegenOptions>::printOptionDiff(
    const Option &, mlir::iree_compiler::GPUCodegenOptions, const OptVal &,
    size_t) const {}
void parser<mlir::iree_compiler::GPUCodegenOptions>::anchor() {}

} // namespace llvm::cl

namespace mlir::iree_compiler {

namespace {

// Applies the opt-level default cascade using a local binder.
template <typename CodegenOptionsT>
void applyOptLevelDefaults(CodegenOptionsT &opts,
                           llvm::OptimizationLevel level) {
  auto binder = OptionsBinder::local();
  // Anchor the opt-level hierarchy so applyOptimizationDefaults can walk it.
  binder.topLevelOpt("opt-level", level);
  opts.bindOptions(binder);
  binder.applyOptimizationDefaults();
}

} // namespace

std::string CodegenOptions::tuningSpecPath = "";
bool CodegenOptions::setTunerAttributes = false;
bool CodegenOptions::verifyPipelineConstraints = false;
bool CodegenOptions::emitPipelineConstraints = false;

void CodegenOptions::bindOptions(OptionsBinder &binder) {
  static llvm::cl::OptionCategory category("IREE Codegen Options");

  binder.opt<std::string>(
      "iree-codegen-tuning-spec-path", tuningSpecPath, llvm::cl::cat(category),
      llvm::cl::desc("Path to a module containing a tuning spec (transform "
                     "dialect library). Accepts MLIR text (.mlir) and "
                     "bytecode (.mlirbc) formats."));

  binder.opt<bool>("iree-codegen-add-tuner-attributes", setTunerAttributes,
                   llvm::cl::cat(category),
                   llvm::cl::desc("Adds attribute for tuner."));

  // Deprecated alias for the old spelling.
  binder.opt<bool>(
      "iree-config-add-tuner-attributes", setTunerAttributes,
      Deprecated("use --iree-codegen-add-tuner-attributes instead"),
      llvm::cl::Hidden, llvm::cl::desc("Adds attribute for tuner."),
      llvm::cl::cat(category));

  binder.opt<bool>(
      "iree-codegen-experimental-verify-pipeline-constraints",
      verifyPipelineConstraints, llvm::cl::cat(category),
      llvm::cl::desc("Emit and verify SMT pipeline constraints for root ops. "
                     "Implies --iree-codegen-add-tuner-attributes."));
  binder.opt<bool>(
      "iree-codegen-emit-pipeline-constraints", emitPipelineConstraints,
      llvm::cl::cat(category),
      llvm::cl::desc("Emit SMT pipeline constraints and keep them in the IR. "
                     "Implies --iree-codegen-add-tuner-attributes. Intended "
                     "for use with "
                     "--compile-to=executable-configurations."));
}

void CPUCodegenOptions::setWithOptLevel(llvm::OptimizationLevel level) {
  applyOptLevelDefaults(*this, level);
}

CPUCodegenOptions
CPUCodegenOptions::getWithOptLevel(llvm::OptimizationLevel level) {
  CPUCodegenOptions opts;
  opts.setWithOptLevel(level);
  return opts;
}

void CPUCodegenOptions::bindOptions(OptionsBinder &binder) {
  static llvm::cl::OptionCategory category("IREE CPU Codegen Options");
  CodegenOptions::bindOptions(binder);

  auto initAtOpt = binder.optimizationLevel(
      "iree-llvmcpu-mlir-opt-level", optLevel,
      llvm::cl::desc("Optimization level for MLIR codegen passes."),
      llvm::cl::cat(category));

  binder.opt<bool>("iree-llvmcpu-disable-distribution", disableDistribution,
                   llvm::cl::desc("Disable thread distribution in codegen."),
                   llvm::cl::cat(category));

  binder.opt<bool>(
      "iree-llvmcpu-fail-on-out-of-bounds-stack-allocation",
      failOnOutOfBoundsStackAllocation,
      llvm::cl::desc("Fail if the upper bound of dynamic stack allocation "
                     "cannot be solved."),
      llvm::cl::cat(category));

  binder.opt<bool>("iree-llvmcpu-reassociate-fp-reductions",
                   reassociateFpReductions,
                   {initAtOpt(llvm::OptimizationLevel::O0, false),
                    initAtOpt(llvm::OptimizationLevel::O2, true)},
                   llvm::cl::desc("Enables reassociation for FP reductions."),
                   llvm::cl::cat(category));

  binder.opt<bool>(
      "iree-llvmcpu-use-fast-min-max-ops", useFastMinMaxOps,
      llvm::cl::desc(
          "Use `arith.minf/maxf` instead of `arith.minimumf/maximumf` ops."),
      llvm::cl::cat(category));

  binder.opt<bool>(
      "iree-llvmcpu-skip-intermediate-roundings", skipIntermediateRoundings,
      llvm::cl::desc(
          "Allow skipping intermediate roundings. For example, in f16 matmul "
          "kernels on targets with only f32 arithmetic, we have to perform "
          "each multiply-accumulate in f32, and if this flag is false, then "
          "we have to round those f32 accumulators to the nearest f16 every "
          "time, which is slow."),
      llvm::cl::cat(category));

  binder.opt<bool>(
      "iree-llvmcpu-use-decompose-softmax-fuse", useSoftmaxInterFusion,
      llvm::cl::desc(
          "Enables inter-pass fusion for the DecomposeSoftmax pass."),
      llvm::cl::cat(category));

  binder.opt<bool>(
      "iree-llvmcpu-instrument-memory-accesses", instrumentMemoryAccesses,
      llvm::cl::desc(
          "Instruments memory reads and writes in dispatches for address "
          "tracking. Use with --iree-hal-instrument-dispatches=<buffer-size> "
          "and analyze results with iree-dump-instruments."),
      llvm::cl::cat(category));

  binder.opt<bool>("iree-llvmcpu-experimental-vectorize-to-transfer-gather",
                   enableTransferGather,
                   llvm::cl::desc("Experimental: enables vectorization to "
                                  "iree_vector_ext.transfer_gather."),
                   llvm::cl::cat(category));
}

void GPUCodegenOptions::setWithOptLevel(llvm::OptimizationLevel level) {
  applyOptLevelDefaults(*this, level);
}

GPUCodegenOptions
GPUCodegenOptions::getWithOptLevel(llvm::OptimizationLevel level) {
  GPUCodegenOptions opts;
  opts.setWithOptLevel(level);
  return opts;
}

void GPUCodegenOptions::bindOptions(OptionsBinder &binder) {
  CodegenOptions::bindOptions(binder);
}

} // namespace mlir::iree_compiler
