#include "SparcTargetMachine.h"
#include "LeonPasses.h"
#include "Sparc.h"
#include "SparcTargetObjectFile.h"
#include "TargetInfo/SparcTargetInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/MC/TargetRegistry.h"
using namespace llvm;
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSparcTarget() {
    RegisterTargetMachine<SparcV8TargetMachine> X(getTheSparcTarget());
  RegisterTargetMachine<SparcV9TargetMachine> Y(getTheSparcV9Target());
  RegisterTargetMachine<SparcelTargetMachine> Z(getTheSparcelTarget());
}
static std::string computeDataLayout(const Triple &T, bool is64Bit) {
    std::string Ret = T.getArch() == Triple::sparcel ? "e" : "E";
  Ret += "-m:e";
    if (!is64Bit)
    Ret += "-p:32:32";
    Ret += "-i64:64";
      if (is64Bit)
    Ret += "-n32:64";
  else
    Ret += "-f128:64-n32";
  if (is64Bit)
    Ret += "-S128";
  else
    Ret += "-S64";
  return Ret;
}
static Reloc::Model getEffectiveRelocModel(Optional<Reloc::Model> RM) {
  return RM.value_or(Reloc::Static);
}
static CodeModel::Model
getEffectiveSparcCodeModel(Optional<CodeModel::Model> CM, Reloc::Model RM,
                           bool Is64Bit, bool JIT) {
  if (CM) {
    if (*CM == CodeModel::Tiny)
      report_fatal_error("Target does not support the tiny CodeModel", false);
    if (*CM == CodeModel::Kernel)
      report_fatal_error("Target does not support the kernel CodeModel", false);
    return *CM;
  }
  if (Is64Bit) {
    if (JIT)
      return CodeModel::Large;
    return RM == Reloc::PIC_ ? CodeModel::Small : CodeModel::Medium;
  }
  return CodeModel::Small;
}
SparcTargetMachine::SparcTargetMachine(
    const Target &T, const Triple &TT, StringRef CPU, StringRef FS,
    const TargetOptions &Options, Optional<Reloc::Model> RM,
    Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT, bool is64bit)
    : LLVMTargetMachine(T, computeDataLayout(TT, is64bit), TT, CPU, FS, Options,
                        getEffectiveRelocModel(RM),
                        getEffectiveSparcCodeModel(
                            CM, getEffectiveRelocModel(RM), is64bit, JIT),
                        OL),
      TLOF(std::make_unique<SparcELFTargetObjectFile>()),
      Subtarget(TT, std::string(CPU), std::string(FS), *this, is64bit),
      is64Bit(is64bit) {
  initAsmInfo();
}
SparcTargetMachine::~SparcTargetMachine() = default;
const SparcSubtarget *
SparcTargetMachine::getSubtargetImpl(const Function &F) const {
  Attribute CPUAttr = F.getFnAttribute("target-cpu");
  Attribute FSAttr = F.getFnAttribute("target-features");
  std::string CPU =
      CPUAttr.isValid() ? CPUAttr.getValueAsString().str() : TargetCPU;
  std::string FS =
      FSAttr.isValid() ? FSAttr.getValueAsString().str() : TargetFS;
        bool softFloat = F.getFnAttribute("use-soft-float").getValueAsBool();
  if (softFloat)
    FS += FS.empty() ? "+soft-float" : ",+soft-float";
  auto &I = SubtargetMap[CPU + FS];
  if (!I) {
                resetTargetOptions(F);
    I = std::make_unique<SparcSubtarget>(TargetTriple, CPU, FS, *this,
                                          this->is64Bit);
  }
  return I.get();
}
namespace {
class SparcPassConfig : public TargetPassConfig {
public:
  SparcPassConfig(SparcTargetMachine &TM, PassManagerBase &PM)
    : TargetPassConfig(TM, PM) {}
  SparcTargetMachine &getSparcTargetMachine() const {
    return getTM<SparcTargetMachine>();
  }
  void addIRPasses() override;
  bool addInstSelector() override;
  void addPreEmitPass() override;
};
} 
TargetPassConfig *SparcTargetMachine::createPassConfig(PassManagerBase &PM) {
  return new SparcPassConfig(*this, PM);
}
void SparcPassConfig::addIRPasses() {
  addPass(createAtomicExpandPass());
  TargetPassConfig::addIRPasses();
}
bool SparcPassConfig::addInstSelector() {
  addPass(createSparcISelDag(getSparcTargetMachine()));
  return false;
}
void SparcPassConfig::addPreEmitPass(){
  addPass(createSparcDelaySlotFillerPass());
  if (this->getSparcTargetMachine().getSubtargetImpl()->insertNOPLoad())
  {
    addPass(new InsertNOPLoad());
  }
  if (this->getSparcTargetMachine().getSubtargetImpl()->detectRoundChange()) {
    addPass(new DetectRoundChange());
  }
  if (this->getSparcTargetMachine().getSubtargetImpl()->fixAllFDIVSQRT())
  {
    addPass(new FixAllFDIVSQRT());
  }
}
void SparcV8TargetMachine::anchor() { }
SparcV8TargetMachine::SparcV8TargetMachine(const Target &T, const Triple &TT,
                                           StringRef CPU, StringRef FS,
                                           const TargetOptions &Options,
                                           Optional<Reloc::Model> RM,
                                           Optional<CodeModel::Model> CM,
                                           CodeGenOpt::Level OL, bool JIT)
    : SparcTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, false) {}
void SparcV9TargetMachine::anchor() { }
SparcV9TargetMachine::SparcV9TargetMachine(const Target &T, const Triple &TT,
                                           StringRef CPU, StringRef FS,
                                           const TargetOptions &Options,
                                           Optional<Reloc::Model> RM,
                                           Optional<CodeModel::Model> CM,
                                           CodeGenOpt::Level OL, bool JIT)
    : SparcTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, true) {}
void SparcelTargetMachine::anchor() {}
SparcelTargetMachine::SparcelTargetMachine(const Target &T, const Triple &TT,
                                           StringRef CPU, StringRef FS,
                                           const TargetOptions &Options,
                                           Optional<Reloc::Model> RM,
                                           Optional<CodeModel::Model> CM,
                                           CodeGenOpt::Level OL, bool JIT)
    : SparcTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, false) {}