//===- SimpleInstrSelEmitter.cpp - Generate a Simple Instruction Selector ------------===//
// 
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
// 
//===----------------------------------------------------------------------===//
//
// This tablegen backend is responsible for emitting an instruction selector
// 
//
//===----------------------------------------------------------------------===//
#include "InstrInfoEmitter.h"
#include "SimpleInstrSelEmitter.h"
#include "CodeGenWrappers.h"
#include "Record.h"
#include "Support/Debug.h"
#include "Support/StringExtras.h"
#include <set>


#include "Record.h"
#include "Support/CommandLine.h"
#include "Support/Signals.h"
#include "Support/FileUtilities.h"
#include "CodeEmitterGen.h"
#include "RegisterInfoEmitter.h"
#include "InstrInfoEmitter.h"
#include "InstrSelectorEmitter.h"
#include "SimpleInstrSelEmitter.h"
#include <algorithm>
#include <cstdio>
#include <fstream>
#include <vector>

namespace llvm {

  std::string FnDecs;

// run - Emit the main instruction description records for the target...
void SimpleInstrSelEmitter::run(std::ostream &OS) {


  //  EmitSourceFileHeader("Mark's Instruction Selector for the X86 target", OS);


//   OS << "#include \"llvm/CodeGen/MachineInstrBuilder.h\"\n";

//   OS << "#include \"llvm/Constants.h\"\n";
//   OS << "#include \"llvm/DerivedTypes.h\"\n";
//   OS << "#include \"llvm/Function.h\"\n";
//   OS << "#include \"llvm/Instructions.h\"\n";
//   OS << "#include \"llvm/IntrinsicLowering.h\"\n";
//   OS << "#include \"llvm/Pass.h\"\n";
//   OS << "#include \"llvm/CodeGen/MachineConstantPool.h\"\n";
//   OS << "#include \"llvm/CodeGen/MachineFrameInfo.h\"\n";
//   OS << "#include \"llvm/CodeGen/MachineFunction.h\"\n";
//   OS << "#include \"llvm/CodeGen/MachineInstrBuilder.h\"\n";
//   OS << "#include \"llvm/CodeGen/SSARegMap.h\"\n";
//   OS << "#include \"llvm/Target/MRegisterInfo.h\"\n";
//   OS << "#include \"llvm/Target/TargetMachine.h\"\n";
//   OS << "#include \"llvm/Support/InstVisitor.h\"\n";

//  OS << "using namespace llvm;\n\n";


  FnDecs = "";

  // for each InstrClass

  std::vector<Record*> Recs = Records.getAllDerivedDefinitions("InstrClass");
  for (unsigned i = 0, e = Recs.size(); i != e; ++i) {
    std::string InstrClassName = Recs[i]->getName();
    OS << "// Generate BMI instructions for " << InstrClassName << "\n\n";
    OS << "void ISel::visit";
    OS << Recs[i]->getValueAsString("FunctionName");
    OS << "(" <<  Recs[i]->getValueAsString("InstructionName") << " &I)\n{" << "\n";
    // for each supported InstrSubclass

    OS << spacing() << "unsigned DestReg = getReg(I);\n";
    OS << spacing() << "unsigned Op0Reg  = getReg(I.getOperand(0));\n";
    OS << spacing() << "unsigned Op1Reg  = getReg(I.getOperand(1));\n";
    OS << spacing() << "Value   *Op0Val  = I.getOperand(0);\n";
    OS << spacing() << "Value   *Op1Val  = I.getOperand(1);\n";

    OS << spacing() << "MachineBasicBlock::iterator IP = BB->end();\n";

    OS << std::endl;

    ListInit *SupportedSubclasses = Recs[i]->getValueAsListInit("Supports");

    //OS << spacing() << InstrClassName << "Prep();" << "\n";
    //FnDecs += "void ISel::" + InstrClassName + "Prep() {\n\n}\n\n";

    std::vector<std::string> vi;

    // generate subclasses nested switch statements
    InstrSubclasses(OS, InstrClassName, InstrClassName, SupportedSubclasses, vi, 0);

    //OS << spacing() << InstrClassName << "Post();\n";
    //FnDecs += "void ISel::" + InstrClassName + "Post() {\n\n}\n\n";

    OS << "}\n";
    OS << "\n\n\n";

  } // for each instrclass

  //  OS << "} //namespace\n";


#if 0
  // print out function stubs
  OS << "\n\n\n//Functions\n\n" << FnDecs;

  // print out getsubclass() definitions
  std::vector<Record*> SubclassColRec = Records.getAllDerivedDefinitions("InstrSubclassCollection");
  for (unsigned j=0, m=SubclassColRec.getSize(); j!=m; ++j) {
    std::string SubclassName = SubclassColRec[j]->getName();
    FnDecs += "unsigned ISel::get" + SubclassName + "() {\n\n";

    ListInit* list = dynamic_cast<ListInit*>(SubclassColRec[j].getValueAsListInit("List"));
    
    for (unsigned k=0; n=list.getSize(); k!=n; ++k) {

    FnDecs += "}\n\n";
  }
#endif

} //run

  

// find instructions that match all the subclasses (only support for 1 now)
Record* SimpleInstrSelEmitter::findInstruction(std::ostream &OS, std::string cl, std::vector<std::string>& vi) {
  std::vector<Record*> Recs = Records.getAllDerivedDefinitions("TargInstrSet");

  for (unsigned i = 0, e = Recs.size(); i != e; ++i) {
    Record* thisClass = Recs[i]->getValueAsDef("Class");

    if (thisClass->getName() == cl) {

      // get the Subclasses this supports
      ListInit* SubclassList = Recs[i]->getValueAsListInit("List");

      bool Match = true;

      if (SubclassList->getSize() != vi.size())
	Match = false;
      
      // match the instruction's supported subclasses with the subclasses we are looking for

      for (unsigned j=0, f=SubclassList->getSize(); j!=f; ++j) {
	DefInit* SubclassDef = dynamic_cast<DefInit*>(SubclassList->getElement(j));
	Record* thisSubclass = SubclassDef->getDef();

	std::string searchingFor = vi[j];

	if (thisSubclass->getName() != searchingFor) {
	  Match = false;
	}

      } // for each subclass list

      if (Match == true) { return Recs[i]; }
  
    } //if instrclass matches

  } // for all instructions

  // if no instructions found, return NULL
  return NULL;

} //findInstruction
  


Record* SimpleInstrSelEmitter::findRegister(std::ostream &OS, std::string regname) {
  std::vector<Record*> Recs = Records.getAllDerivedDefinitions("Register");

  for (unsigned i = 0, e = Recs.size(); i != e; ++i) {
    Record* thisReg = Recs[i];

    if (thisReg->getName() == regname) return Recs[i];
  }
  
  return NULL;

}

// handle "::" and "+" etc
std::string SimpleInstrSelEmitter::formatRegister(std::ostream &OS, std::string regname) {
  std::string Reg;
  std::string suffix;

  int x = std::strcspn(regname.c_str(),"+-");

  // operate on text before "+" or "-", append it back at the end
  Reg = regname.substr(0,x);
  suffix = regname.substr(x,regname.length());

  unsigned int y = std::strcspn(Reg.c_str(),":");

  if (y == Reg.length()) { // does not contain "::"
    
    Record* RegRec = findRegister(OS,Reg);

    assert(RegRec && "Register not found!");

    if (RegRec->getValueAsString("Namespace") != "Virtual") {
      Reg = RegRec->getValueAsString("Namespace") + "::" + RegRec->getName();
    } else {
      Reg = RegRec->getName();
    }
  } // regular case

  // append + or - at the end again (i.e. X86::EAX+1)
  Reg = Reg + suffix;

  return Reg;
}


// take information in the instruction class and generate the correct BMI call
void SimpleInstrSelEmitter::generateBMIcall(std::ostream &OS, std::string MBB, std::string IP, std::string Opcode, int NumOperands, ListInit &instroperands, ListInit &operands) {

  // find Destination Register
  StringInit* DestRegStr = dynamic_cast<StringInit*>(operands.getElement(0));
  std::string DestReg = formatRegister(OS,DestRegStr->getValue());

  OS << "BuildMI(";
  OS << MBB << ", ";
  OS << IP << ", ";
  OS << Opcode << ", ";
  OS << NumOperands;

  if (DestReg != "Pseudo") {
    OS << ", " << DestReg << ")";
  } else {
    OS << ")";
  }
  
  // handle the .add stuff
  for (unsigned i=0, e=instroperands.getSize(); i!=e; ++i) {
    DefInit* OpDef = dynamic_cast<DefInit*>(instroperands.getElement(i));
    StringInit* RegStr = dynamic_cast<StringInit*>(operands.getElement(i+1));

    Record* Op = OpDef->getDef();

    std::string opstr = Op->getValueAsString("Name");

    std::string regname;

    if (opstr == "Register") {
      regname = formatRegister(OS,RegStr->getValue());
    } else {
      regname = RegStr->getValue();
    }

    OS << ".add" << opstr << "(" << regname << ")";
  }
  
  OS << ";\n";
  
} //generateBMIcall

  
 std::string SimpleInstrSelEmitter::spacing() {
   return globalSpacing;
 }

 std::string SimpleInstrSelEmitter::addspacing() {
   globalSpacing += "  ";
   return globalSpacing;
 }

 std::string SimpleInstrSelEmitter::remspacing() {
   globalSpacing = globalSpacing.substr(0,globalSpacing.length()-2);
   return globalSpacing;
 }


 // recursively print out the subclasses of an instruction
 //
 void SimpleInstrSelEmitter::InstrSubclasses(std::ostream &OS, std::string prefix, std::string InstrClassName, ListInit* SupportedSubclasses, std::vector<std::string>& vi, unsigned depth) {

   
   if (depth >= SupportedSubclasses->getSize()) {
      return;
   }

   // get the subclass collection
   
   DefInit* InstrSubclassColl = dynamic_cast<DefInit*>(SupportedSubclasses->getElement(depth));

   Record* InstrSubclassRec = InstrSubclassColl->getDef();

   std::string SubclassName = InstrSubclassRec->getName();
   
   
   if (InstrSubclassRec->getValueAsString("PreCode") != "") {
     //	  OS << spacing() << prefix << "_" << Subclass->getName() << "_Prep();\n";
     OS << spacing() << InstrSubclassRec->getValueAsString("PreCode") << "\n\n";
   }
   
   
   OS << spacing() << "// Looping through " << SubclassName << "\n";
   
   OS << spacing() << "switch (" <<  SubclassName <<") {\n";
   addspacing();
   
   ListInit* SubclassList = InstrSubclassRec->getValueAsListInit("List");
   
   for (unsigned k=0, g = SubclassList->getSize(); k!=g; ++k) {
     
     DefInit* SubclassDef = dynamic_cast<DefInit*>(SubclassList->getElement(k));
     
     Record* Subclass = SubclassDef->getDef();
     
     OS << spacing() << "// " << prefix << "_" << Subclass->getName() << "\n";
     OS << spacing() << "case " << Subclass->getName() << ":\n";
     addspacing();
     OS << spacing() << "{\n";
     
     
     vi.push_back(Subclass->getName());
     
     // go down hierarchy
     InstrSubclasses(OS, prefix + "_" + Subclass->getName(), InstrClassName, SupportedSubclasses, vi, depth+1);
     
     // find the record that matches this
     Record *theInstructionSet = findInstruction(OS, InstrClassName, vi);
     
     // only print out the assertion if this is a leaf
     if ( (theInstructionSet == NULL) && (depth == (SupportedSubclasses->getSize() - 1)) ) {
       
       OS << spacing() << "assert(0 && \"No instructions defined for " << InstrClassName << " instructions of subclasses " << prefix << "_" << Subclass->getName() << "!\");" << "\n";
       
     } else if (theInstructionSet != NULL) {
       
       if (theInstructionSet->getValueAsString("PreCode") != "") {
	 OS << spacing() << theInstructionSet->getValueAsString("PreCode") << "\n\n";
       }
       
       ListInit *theInstructions = theInstructionSet->getValueAsListInit("Instructions");
       
       ListInit *registerlists = theInstructionSet->getValueAsListInit("Operands"); // not necessarily registers anymore, but the name will stay for now
       
       for (unsigned l=0, h=theInstructions->getSize(); l!=h; ++l) {
	 
	 DefInit *theInstructionDef = dynamic_cast<DefInit*>(theInstructions->getElement(l));
	 Record *theInstruction = theInstructionDef->getDef();
	 
	 ListInit *operands = theInstruction->getValueAsListInit("Params");
	 
	 OS << spacing();
	 
	 ListInit* registers = dynamic_cast<ListInit*>(registerlists->getElement(l));
	 
	 // handle virtual instructions here before going to generateBMIcall
	 
	 if (theInstruction->getValueAsString("Namespace") == "Virtual") {
	   
	   // create reg for different sizes
	   std::string Instr = theInstruction->getName();
	   StringInit* DestRegInit = dynamic_cast<StringInit*>(registers->getElement(0));
	   std::string DestReg = DestRegInit->getValue();
	   std::string theType;
	   
	   if (Instr == "NullInstruction") { } // do nothing
	   else if (Instr == "CreateRegByte")
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::SByteTy);\n";
	   else if (Instr == "CreateRegShort")
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::SShortTy);\n";
	   else if (Instr == "CreateRegInt")
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::SIntTy);\n";
	   else if (Instr == "CreateRegLong")
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::SLongTy);\n";
	   else if (Instr == "CreateRegUByte")
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::UByteTy);\n";
	   else if (Instr == "CreateRegUShort")
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::UShortTy);\n";
	   else if (Instr == "CreateRegUInt")
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::UIntTy);\n";
	   else if (Instr == "CreateRegULong") 
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::ULongTy);\n";
	   else if (Instr == "CreateRegFloat") 
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::FloatTy);\n";
	   else if (Instr == "CreateRegDouble") 
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::DoubleTy);\n";
	   else if (Instr == "CreateRegPointer") 
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::PointerTy_;\n";
	   else 
	     OS << "unsigned " << DestReg << " = makeAnotherReg(Type::SByteTy);\n"; // create a byte by default
	   
	   
	 } else {
	   std::string InstrName;
	   
	   if (theInstruction->getValueAsString("Namespace") != "Virtual") {
	     InstrName = theInstruction->getValueAsString("Namespace") + "::" + theInstruction->getValueAsString("Name");
	   } else {
	     // shouldn't ever happen, virtual instrs should be caught before this
	     InstrName = theInstruction->getValueAsString("Name");
	   }
	   
	   generateBMIcall(OS, "*BB","IP",InstrName,theInstruction->getValueAsInt("NumOperands"),*operands,*registers);
	 }
	 
       }
       
       if (theInstructionSet->getValueAsString("PostCode") != "") {
	 OS << spacing() << theInstructionSet->getValueAsString("PostCode") << "\n\n";
       }
       
     }
     
     
     
     if (InstrSubclassRec->getValueAsString("PostCode") != "") {
       //OS << spacing() << "// " << prefix << "_" << Subclass->getName() << "_Prep();\n";
       OS << spacing() << InstrSubclassRec->getValueAsString("PostCode") << "\n\n";
     }
     
     
     OS << spacing() << "break;\n";

     OS << spacing() << "}\n\n";
     
     remspacing();
     
     vi.pop_back();
   }
   
   // provide a default case for the switch
   
   OS << spacing() << "default:\n";
   OS << spacing() << "  assert(0 && \"No instructions defined for " << InstrClassName << " instructions of subclasses " << prefix << "_" << SubclassName << "!\");" << "\n";
   OS << spacing() << "  break;\n\n";
   
   remspacing();
   OS << spacing() << "}\n";
   
 }


 // ret br switch invoke unwind
 // add sub mul div rem setcc (eq ne lt gt le ge)
 // and or xor sbl sbr
 // malloc free alloca load store
 // getelementptr phi cast call vanext vaarg
 
} // End llvm namespace
