#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/CodeGen/CodeGenAction.h"
#include "clang/CodeGen/BackendUtil.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/BasicBlock.h"
#include "llvm/Config/config.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Assembly/AssemblyAnnotationWriter.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AliasSetTracker.h"
#include "llvm/PassAnalysisSupport.h"
#include "llvm/PassManager.h"
#include <unistd.h>
#include <iostream>
#include <string>

using namespace llvm;
using namespace clang;


static llvm::cl::list<std::string>
InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input source files>"));

static llvm::cl::list<std::string>
InputHeaders("I", llvm::cl::desc("Additional include paths"));


namespace {

	class DummyPrinterPass;

	class DummyPrinterPass : public BasicBlockPass {
		public:
			static char ID;
			std::string PassName;
			std::string Banner;
			raw_ostream &Out;

			DummyPrinterPass(std::string& banner, raw_ostream &o)
				: BasicBlockPass(ID), Banner(banner), Out(o) {

				PassName = "Dummy PrinterPass";
			};


		  virtual bool runOnBasicBlock(BasicBlock &BB) {
			  AssemblyAnnotationWriter formatter;

			  Out << Banner;
			  BB.print(Out, &formatter);
			  return false;
		  }

		  virtual const char *getPassName() const {
			  return PassName.c_str();
		  }

		  virtual Pass* createPrinterPass(raw_ostream &O,
											const std::string &Banner) const {
			return new DummyPrinterPass(Banner, O);
		  }
	};

	class AliasTrackerPass : public BasicBlockPass {
		private:
			AliasSetTracker*	alias_tracker;

		public:
			static char ID;
			std::string PassName;

			AliasTrackerPass(AliasSetTracker* tracker)
				: BasicBlockPass(ID), alias_tracker(tracker) {

				PassName = "BasicBlockPass AliasTracker";
			};


		  virtual bool runOnBasicBlock(BasicBlock &BB) {
			  alias_tracker->add(BB);
			  return false;
		  }

		  virtual const char *getPassName() const {
			  return PassName.c_str();
		  }

		  virtual void getAnalysisUsage(AnalysisUsage &AU) const {
			  AU.addRequired<AliasAnalysis>();
			  AU.setPreservesAll();
		  }

		  virtual Pass* createPrinterPass(raw_ostream &O,
		                                    const std::string &Banner) const {
		    return new DummyPrinterPass(Banner, O);
		  }

	};

	char AliasTrackerPass::ID = 0;


	class AliasDetectionPass : public BasicBlockPass {
		private:
			AliasSetTracker*	alias_tracker;

		public:
			static char ID;
			std::string PassName;

			AliasDetectionPass(AliasSetTracker* tracker)
				: BasicBlockPass(ID), alias_tracker(tracker) {

				PassName = "BasicBlockPass AliasDetection";
			};


		  virtual bool runOnBasicBlock(BasicBlock &BB) {

			  AssemblyAnnotationWriter formatter;

			  for (BasicBlock::const_iterator	I = BB.begin(); I != BB.end(); ++I) {

				  for (unsigned int i = 0; i < I->getNumOperands(); i++) {

					  Value* val = I->getOperand(i);

					  if (val->getType()->isArrayTy()
					    || val->getType()->isStructTy()
						|| val->getType()->isPointerTy()
						|| val->getType()->isVectorTy()) {


						  AliasSet* alias_set = alias_tracker->getAliasSetForPointerIfExists(val, 0, 0);

						  // print alias sets for '*I'
						  llvm::errs() << "=== Alias set for '";
						  val->print(llvm::errs(), &formatter);
						  llvm::errs() << "'" << '\n';

						  if (alias_set) {
							  for (AliasSet::iterator I = alias_set->begin(); alias_set->end() != I; ++I) {
								  I->getValue()->print(llvm::errs(), &formatter);
								  llvm::errs() << '\n';
							  }
						  } else {
							  llvm::errs() << "No aliases" << '\n';
						  }
					  }
				  }
			  }
			  return false;
		  }

		  virtual const char *getPassName() const {
			  return PassName.c_str();
		  }

		  virtual void getAnalysisUsage(AnalysisUsage &AU) const {
			  AU.addRequired<AliasTrackerPass>();
			  AU.addPreserved<AliasAnalysis>();
		  }

		  virtual Pass* createPrinterPass(raw_ostream &O,
		                                    const std::string &Banner) const {
		    return new DummyPrinterPass(Banner, O);
		  }
	};

	char AliasDetectionPass::ID = 0;
}

int main(int argc, char **argv) {
	llvm::cl::ParseCommandLineOptions(argc, argv, "alias-detector");


	CompilerInstance clang;
	LangOptions lng_opt;

	clang.getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
	clang.getInvocation().setLangDefaults(lng_opt, IK_C, LangStandard::lang_gnu9x);

	clang.createDiagnostics(argc, argv,
		new TextDiagnosticPrinter(
				llvm::errs(), &(clang.getDiagnosticOpts())
		)
	);


	char *path = NULL;
	size_t size = 0;

	path = getcwd(path,size);
	if (!path)
	return 1;

	std::string buffer(path);
	free(path);

	clang.getInvocation().getFileSystemOpts().WorkingDir = buffer;


	clang.getHeaderSearchOpts().UseBuiltinIncludes = 1;
	clang.getHeaderSearchOpts().UseStandardSystemIncludes = 1;
	clang.getHeaderSearchOpts().ResourceDir = LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;

	for (unsigned i = 0, e = InputHeaders.size(); i != e; ++i) {
		clang.getHeaderSearchOpts().AddPath(InputHeaders[i], clang::frontend::Angled, true, false, false);
	}


	clang.getCodeGenOpts().OptimizationLevel = 0;
	clang.getCodeGenOpts().OptimizeSize = 0;
	clang.getCodeGenOpts().DisableLLVMOpts = 1;
	clang.getCodeGenOpts().MergeAllConstants = 1;
	clang.getCodeGenOpts().UnrollLoops = 1;
	clang.getCodeGenOpts().AsmVerbose = 1;

	clang.getFrontendOpts().ProgramAction = frontend::EmitLLVMOnly;
	clang.getFrontendOpts().ARCMTAction = FrontendOptions::ARCMT_None;

	clang.getFrontendOpts().ShowStats = 1;

	for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) {
		InputKind IK = IK_None;
		IK = FrontendOptions::getInputKindForExtension(
				StringRef(InputFilenames[i]).rsplit('.').second
		);
		clang.getFrontendOpts().Inputs.push_back(FrontendInputFile(InputFilenames[i], IK));
	}


	OwningPtr<CodeGenAction> act(new EmitLLVMOnlyAction());
	clang.ExecuteAction(*act);

	llvm::Module* mod = act->takeModule();

	PassManager		pass_mgr;
	ModulePass*		basic_aa = createBasicAliasAnalysisPass();
	AliasSetTracker	alias_tracker(*((AliasAnalysis*)basic_aa));

	// Actually execute the alias analysis
	pass_mgr.add(basic_aa);

	// Add all basic blocks to the AliasSetTracker / create the alias sets
	pass_mgr.add(new AliasTrackerPass(&alias_tracker));

	// Inspect all values (of CompositeType) for aliases
	pass_mgr.add(new AliasDetectionPass(&alias_tracker));

	// Execute the passes
	pass_mgr.run(*mod);


	llvm::llvm_shutdown();

	return 0;
}
