//===- subzero/src/IceSwitchLowering.h - Switch lowering --------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Helpers for switch lowering.
///
//===----------------------------------------------------------------------===//

#ifndef SUBZERO_SRC_ICESWITCHLOWERING_H
#define SUBZERO_SRC_ICESWITCHLOWERING_H

#include "IceDefs.h"
#include "IceStringPool.h"

#include <string>

namespace Ice {

class CaseCluster;

using CaseClusterArray = CfgVector<CaseCluster>;

/// A cluster of cases can be tested by a common method during switch lowering.
class CaseCluster {
  CaseCluster() = delete;

public:
  enum CaseClusterKind {
    Range,     /// Numerically adjacent case values with same target.
    JumpTable, /// Different targets and possibly sparse.
  };

  CaseCluster(const CaseCluster &) = default;
  CaseCluster &operator=(const CaseCluster &) = default;

  /// Create a cluster of a single case represented by a unitary range.
  CaseCluster(uint64_t Value, CfgNode *Target)
      : Kind(Range), Low(Value), High(Value), Target(Target) {}
  /// Create a case consisting of a jump table.
  CaseCluster(uint64_t Low, uint64_t High, InstJumpTable *JT)
      : Kind(JumpTable), Low(Low), High(High), JT(JT) {}

  CaseClusterKind getKind() const { return Kind; }
  uint64_t getLow() const { return Low; }
  uint64_t getHigh() const { return High; }
  CfgNode *getTarget() const {
    assert(Kind == Range);
    return Target;
  }
  InstJumpTable *getJumpTable() const {
    assert(Kind == JumpTable);
    return JT;
  }

  bool isUnitRange() const { return Low == High; }
  bool isPairRange() const { return Low == High - 1; }

  /// Discover cases which can be clustered together and return the clusters
  /// ordered by case value.
  static CaseClusterArray clusterizeSwitch(Cfg *Func, const InstSwitch *Instr);

private:
  CaseClusterKind Kind;
  uint64_t Low;
  uint64_t High;
  union {
    CfgNode *Target;   /// Target for a range.
    InstJumpTable *JT; /// Jump table targets.
  };

  /// Try and append a cluster returning whether or not it was successful.
  bool tryAppend(const CaseCluster &New);
};

/// Store the jump table data so that it can be emitted later in the correct ELF
/// section once the offsets from the start of the function are known.
class JumpTableData {
  JumpTableData() = delete;
  JumpTableData &operator=(const JumpTableData &) = delete;

public:
  using TargetList = std::vector<intptr_t>;

  JumpTableData(GlobalString Name, GlobalString FuncName, SizeT Id,
                const TargetList &TargetOffsets)
      : Name(Name), FuncName(FuncName), Id(Id), TargetOffsets(TargetOffsets) {}
  JumpTableData(const JumpTableData &) = default;
  JumpTableData(JumpTableData &&) = default;
  JumpTableData &operator=(JumpTableData &&) = default;

  GlobalString getName() const { return Name; }
  GlobalString getFunctionName() const { return FuncName; }
  SizeT getId() const { return Id; }
  const TargetList &getTargetOffsets() const { return TargetOffsets; }
  static std::string createSectionName(const GlobalString Name) {
    if (Name.hasStdString()) {
      return Name.toString() + "$jumptable";
    }
    return std::to_string(Name.getID()) + "$jumptable";
  }
  std::string getSectionName() const { return createSectionName(FuncName); }

private:
  GlobalString Name;
  GlobalString FuncName;
  SizeT Id;
  TargetList TargetOffsets;
};

using JumpTableDataList = std::vector<JumpTableData>;

} // end of namespace Ice

#endif //  SUBZERO_SRC_ICESWITCHLOWERING_H