//=- AOCCVerifySupport.h - AOCC utils for verifying transformations -*- 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
//
//===----------------------------------------------------------------------===//
// Modifications Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
// Notified per clause 4(b) of the license.
//
//===----------------------------------------------------------------------===//
//
// This file defines the legacy PassManager class.  This class is used to hold,
// maintain, and optimize execution of Passes.  The PassManager class ensures
// that analysis results are available before a pass runs, and that Pass's are
// destroyed when the PassManager is destroyed.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_IR_AOCCVERIFYSUPPORT_H
#define LLVM_IR_AOCCVERIFYSUPPORT_H
#if AOCC_BUILD
#include "llvm/Pass.h"
#include "llvm/IR/PassInstrumentation.h"
#include "llvm/Support/CommandLine.h"
#include <map>

namespace llvm {
namespace aocc {

  bool verify(const Function& F);
  bool verify(const Module& M);
  bool TBAAModuleMetrics(const Module& M);
  using namespace llvm;
#if AOCC_DEVELOPER_BUILD
extern cl::opt<bool> VerifyAllPasses, VerifyAllPassesP, VerifyAllPassesX, VerifyAllPassesA, VerifyAllPassesM, VerifyAllPassesF, VerifyAllPassesS, VerifyAllPassesV;
extern cl::opt<std::string> VerifyAllControl;
extern int VerifyModuleCount, VerifyFunctionCount;

extern cl::opt<bool> PrintModuleTBAACount;
#define MODULE_TBAA_METRICS (bool(aocc::PrintModuleTBAACount))

#define VERIFY_ENV (getenv("LLVM_VERIFY_ALL"))
#define VERIFY_OPT (bool(aocc::VerifyAllPasses))
#define DO_VERIFY (VERIFY_OPT || VERIFY_ENV)
#define VERIFY_FLAG_ON(FF) ((VERIFY_OPT && aocc::VerifyAllControl.find(FF) != std::string::npos) || \
                            (!VERIFY_OPT && VERIFY_ENV && strchr(VERIFY_ENV, FF)))
#define VERIFY_FLAG_ON_OPT(FF, IRFLAG) (VERIFY_FLAG_ON(FF) || (VERIFY_OPT && llvm::IRFLAG))
#define DO_PRINT_ON_FAIL VERIFY_FLAG_ON_OPT('p', aocc::VerifyAllPassesP)
#define DO_EXIT_ON_FAIL  VERIFY_FLAG_ON_OPT('x', aocc::VerifyAllPassesX)
#define DO_VERIFY_ALWAYS VERIFY_FLAG_ON_OPT('a', aocc::VerifyAllPassesA)
#define DO_VERIFY_MODULE VERIFY_FLAG_ON_OPT('m', aocc::VerifyAllPassesM)
#define DO_VERIFY_FUNCTION VERIFY_FLAG_ON_OPT('f', aocc::VerifyAllPassesF)
#define DO_VERIFY_PRINT_STATS VERIFY_FLAG_ON_OPT('s', aocc::VerifyAllPassesS)
#define DO_VERIFY_PRINT_VERBOSE VERIFY_FLAG_ON_OPT('v', aocc::VerifyAllPassesV)

#define DO_VERIFY_FUNCTION_ACTION(F,PS)                                 \
  if (DO_VERIFY_PRINT_VERBOSE) { \
    dbgs() << "AOCC-VERIFY [verbose]: VERIFY FUNCTION:" << (F).getName() << ",PASS:" << PS << '\n'; \
  }; \
  if (aocc::verify(F)) { \
    ++aocc::VerifyFunctionCount; \
    dbgs() << "VERIFY FAIL FOR: " << (F).getName() << ",PASS:" << PS << '\n'; \
    if (DO_PRINT_ON_FAIL) { \
      dbgs() << "FUNCTION: " << (F) << '\n'; \
    }; \
    if (DO_EXIT_ON_FAIL) assert(0); \
  } else { ++aocc::VerifyFunctionCount; }

#define DO_VERIFY_MODULE_ACTION(M,PS)                                   \
  if (MODULE_TBAA_METRICS) {                                            \
    dbgs() << "AOCC-TBAA-METRIC [verbose]: " << (M).getName() << ",PASS:" << PS << '\n'; \
    aocc::TBAAModuleMetrics(M);                                         \
  }                                                                     \
  if (DO_VERIFY_PRINT_VERBOSE) { \
    dbgs() << "AOCC-VERIFY [verbose]: VERIFY MODULE:" << (M).getName() << ",PASS:" << PS << '\n'; \
  };\
  if (aocc::verify(M)) {                                       \
    ++aocc::VerifyModuleCount; \
    dbgs() << "VERIFY FAIL FOR: " << (M).getName() << ",PASS:" << PS << '\n'; \
    if (DO_PRINT_ON_FAIL) {                                             \
      dbgs() << "MODULE: " << (M) << '\n';                              \
    };                                                                  \
    if (DO_EXIT_ON_FAIL) assert(0);                                     \
  } else { ++aocc::VerifyModuleCount; }

#define CHECK_PASS_MS(C, M, PS)                                         \
  if (DO_VERIFY && (DO_VERIFY_ALWAYS || (C)))  {                        \
    if (DO_VERIFY_FUNCTION) {                                             \
      for (const Function& F : M) {                                       \
        DO_VERIFY_FUNCTION_ACTION(F, PS);                                 \
      }                                                                   \
    }                                                                     \
    DO_VERIFY_MODULE_ACTION(M,PS)                                         \
  } else { }

#define CHECK_PASS_FS(C, F, PS) \
  if (DO_VERIFY && (DO_VERIFY_ALWAYS || (C))) { \
    DO_VERIFY_FUNCTION_ACTION(F,PS)             \
  } else if (DO_VERIFY_MODULE) { CHECK_PASS_MS(C, *((F).getParent()), PS); } else { }

#define CHECK_PASS_F(C, F, P) CHECK_PASS_FS(C, F, (P)->getPassName())
#define CHECK_PASS_M(C, M, P) CHECK_PASS_MS(C, M, (P)->getPassName())

#define CHECK_M(C, M) \
  if (DO_VERIFY && (DO_VERIFY_ALWAYS || (C))) { \
    DO_VERIFY_MODULE_ACTION(M,"(unknown pass)") \
  } else { }

class AOCC_CHECK_PASS_IN_SCOPE {
  PassInstrumentation& PI;
public:
  AOCC_CHECK_PASS_IN_SCOPE(PassInstrumentation& PI);
  ~AOCC_CHECK_PASS_IN_SCOPE();
};

#ifndef AOCC_VERIFY_CONTROL
#define AOCC_VERIFY_CONTROL 0
#endif
#define AOCC_VERIFY_CALL_REQUIRES_DEBUGINFO (1 << 0)

#else
#define CHECK_M(C,M)
#define CHECK_PASS_M(C,M,P)
#define CHECK_PASS_F(C,F,P)
#define CHECK_PASS_FS(C,F,S)
#define CHECK_PASS_MS(C,M,S)
#define DO_VERIFY false
class AOCC_CHECK_PASS_IN_SCOPE {
public:
  AOCC_CHECK_PASS_IN_SCOPE(PassInstrumentation& PI) { }
  ~AOCC_CHECK_PASS_IN_SCOPE() { }
};
#define AOCC_VERIFY_CONTROL 0xffff
#define AOCC_VERIFY_CALL_REQUIRES_DEBUGINFO 0 // turn off this verifier check due to CPUPC-7009
#endif

} // end namespace aocc
} // end namespace llvm

#define AOCC_VERIFY_CONTROL_BIT_NAME(X, Y) X##Y
#define AOCC_VERIFY_CONTROL_BIT(CCC) AOCC_VERIFY_CONTROL_BIT_NAME(AOCC_VERIFY_, CCC)
#define AOCC_IS_VERIFY_ENABLED(CCC) ((AOCC_VERIFY_CONTROL) & (AOCC_VERIFY_CONTROL_BIT(CCC)))

namespace llvm {
namespace aocc {

class OptAOCCFlowConfig {
  void init(StringRef File);
public:
  OptAOCCFlowConfig();
  OptAOCCFlowConfig(StringRef configFile);
  void registerCallbacks(PassInstrumentationCallbacks &PIC);

  struct IREntity {
    Any IR;
    std::string Fun;
    std::string File;
    std::string irtest;
    unsigned Line = 0;
    std::string str() const;
  };

  template<class T>
  struct OptOverride {
    cl::opt<T> *opt;
    T newValue;
    T oldValue;
  };

  struct Test {
    typedef function_ref<bool (StringRef optName, Any IR)> FCTest0;
    typedef function_ref<bool ()> FCTest1;
    struct Tests {
      std::vector<FCTest0> t0;
      std::vector<FCTest1> t1;
    };
    static std::map<std::string, Tests> fcs;
    static bool hasTest(StringRef n);
    static void registerTest(StringRef n, FCTest0 fct);
    static void registerTest(StringRef n, FCTest1 fct);
    static void deregisterTest(StringRef n);
    std::string nn;
    Test(StringRef n, FCTest0 fct) {
      nn = n.str();
      fcs[nn].t0.push_back(fct);
    }
    Test(StringRef n, FCTest1 fct) {
      nn = n.str();
      fcs[nn].t1.push_back(fct);
    }
    ~Test() { fcs.erase(nn); }
  };

  struct OptIRControl {
    IREntity entity;
    std::string flowcxt;
    std::string opt;
    enum { DISABLE_IF, DISABLE_IFNOT, REQUIRE_IF, REQUIRE_IFNOT, CONFIGURE } action;
    bool disable = false;
    std::string str() const;

    std::string config;
    std::vector<OptOverride<bool>> boolConfigOpts;
    std::vector<OptOverride<int>> intConfigOpts;
    std::vector<OptOverride<unsigned>> uintConfigOpts;
    std::vector<OptOverride<std::string>> stringConfigOpts;
  };
  const std::vector<OptIRControl>& cs() const { return c; }
  bool shouldRun(StringRef optName, Any IR);
  bool mustRun(StringRef optName, Any IR);
  void changePassOpts(StringRef optName, Any IR);
  void restorePassOpts(StringRef optName, Any IR);
private:
  std::vector<OptIRControl> c;
  bool shouldRun(StringRef optName, const IREntity& IRE);
  bool mustRun(StringRef optName, const IREntity& IRE);
  void changePassOpts(StringRef optName, const IREntity& IRE);
  void restorePassOpts(StringRef optName, const IREntity& IRE);
};
void setAOCCFlowControl(OptAOCCFlowConfig *);
void resetAOCCFlowControl();
}
}
#endif
#endif
