//===- llvm/Analysis/ClassHierarchyAnalysis.h - CXX Class Hierarchy Analysis
//---*- 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 CXX Class Hierarchy analysis pass.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_ANALYSIS_CLASSHIERARCHYANALYSIS_H
#define LLVM_ANALYSIS_CLASSHIERARCHYANALYSIS_H
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#include <set>

namespace llvm {
class CXXClassInfo {
  bool HasPublic;
  bool HasPrivate;
  bool HasProtected;
  bool HasFriends;
  bool HasInheritedPublic = 0;
  bool HasInheritedProtected = 0;

public:
  std::vector<llvm::GlobalVariable *> Bases;
  std::vector<llvm::GlobalVariable *> VBases;
  std::vector<std::pair<Function *, int>> Members;
  std::vector<Function *> Constructors;
  std::vector<Function *> Destructors;
  std::vector<Function *> CopyConstructors;
  std::vector<StringRef> ConstructorNames;
  std::vector<StringRef> CopyConstructorNames;
  std::vector<StringRef> DestructorNames;
  std::vector<StringRef> FunctionNames;
  CXXClassInfo(GlobalVariable *GV, Module *M) { populateCXXClassInfo(GV, M); }
  void populateCXXClassInfo(GlobalVariable *GV, Module *M);
  void populateCXXClassMembers(GlobalVariable *GV);
  void populateCXXClassMethods(GlobalVariable *GV, Module *M);
  void populateCXXClassBases(GlobalVariable *GV, Module *M);
  void populateInheritedProperties();

  ~CXXClassInfo() {}
  bool hasInheritedPublicMembers() { return HasInheritedPublic; }

  bool hasInheritedProtectedMembers() { return HasInheritedProtected; }

  bool hasPublicMembers() { return HasPublic; }

  bool hasPrivateMembers() { return HasPrivate; }

  bool hasProtectedMembers() { return HasProtected; }

  bool hasFriendFunctions() { return HasFriends; }

  void setHasPublicMembers(bool Val) { HasPublic = Val; }

  void setHasPrivateMembers(bool Val) { HasPrivate = Val; }

  void setHasProtectedMembers(bool Val) { HasProtected = Val; }

  void setHasFriends(bool Val) { HasFriends = Val; }

  void setHasInheritedPublicMembers(bool Val) { HasInheritedPublic = Val; }

  void setHasInheritedProtectedMembers(bool Val) {
    HasInheritedProtected = Val;
  }
};

class ClassHierarchyAnalysis {
public:
  std::map<llvm::GlobalVariable *, CXXClassInfo *> CXXClassInfoMap;
  std::map<CXXClassInfo *, llvm::GlobalVariable *> GlobalCXXMap;
  std::map<CXXClassInfo *,  std::set<CXXClassInfo *>> CXXBaseClassSuperSets;
  bool BaseClassSetsPopulated = false;
  std::map<CXXClassInfo *, std::set<CXXClassInfo *>> CXXDerivedClasses;
  std::map<llvm::Type *, std::set<llvm::GlobalVariable *>> CXXTypeMetaDataMap;
  void run(Module &M, ModuleAnalysisManager &AM);
  void populateBaseClassMap();
  bool isaBaseClass(GlobalVariable *GV1, GlobalVariable *GV2);
  void traverseCXXClassInfo(CXXClassInfo * CurrentClass, std::set<CXXClassInfo *> Visited);
  bool invalidate(Module &M, const PreservedAnalyses &PA,
                  ModuleAnalysisManager::Invalidator &Inv);
  void getDerivedClasses(GlobalVariable *GV, std::set<GlobalVariable *> &GVs);
  void getBaseClasses(GlobalVariable *GV, std::set<GlobalVariable *> &BGVs);
};

class ClassHierarchyAnalysisPass
    : public AnalysisInfoMixin<ClassHierarchyAnalysisPass> {
  friend AnalysisInfoMixin<ClassHierarchyAnalysisPass>;
  ClassHierarchyAnalysis CA;
  static AnalysisKey Key;

public:
  using Result = ClassHierarchyAnalysis;
  Result run(Module &, ModuleAnalysisManager &AM);
};
} // namespace llvm
#endif
