//===-- SPUInstrFormats.td - Cell SPU Instruction Formats --*- tablegen -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
//
// Cell SPU instruction formats. Note that these are notationally similar to
// PowerPC, like "A-Form". But the sizes of operands and fields differ.

// This was kiped from the PPC instruction formats (seemed like a good idea...)

class SPUInstr<dag OOL, dag IOL, string asmstr, InstrItinClass itin>
        : Instruction {
  field bits<32> Inst;

  let Namespace = "SPU";
  let OutOperandList = OOL;
  let InOperandList = IOL;
  let AsmString = asmstr;
  let Itinerary = itin;
}

// RR Format
class RRForm<bits<11> opcode, dag OOL, dag IOL, string asmstr, 
              InstrItinClass itin, list<dag> pattern>
         : SPUInstr<OOL, IOL, asmstr, itin> {
  bits<7> RA;
  bits<7> RB;
  bits<7> RT;

  let Pattern = pattern;

  let Inst{0-10} = opcode;
  let Inst{11-17} = RB;
  let Inst{18-24} = RA;
  let Inst{25-31} = RT;
}

let RB = 0 in {
  // RR Format, where RB is zeroed (dont care):
  class RRForm_1<bits<11> opcode, dag OOL, dag IOL, string asmstr, 
                 InstrItinClass itin, list<dag> pattern>
           : RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
  { }

  let RA = 0 in {
    // RR Format, where RA and RB are zeroed (dont care):
    // Used for reads from status control registers (see FPSCRRr32)
    class RRForm_2<bits<11> opcode, dag OOL, dag IOL, string asmstr,
                   InstrItinClass itin, list<dag> pattern>
             : RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
    { }
  }
}

let RT = 0 in {
  // RR Format, where RT is zeroed (don't care), or as the instruction handbook
  // says, "RT is a false target." Used in "Halt if" instructions
  class RRForm_3<bits<11> opcode, dag OOL, dag IOL, string asmstr,
                 InstrItinClass itin, list<dag> pattern>
      : RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
  { }
}

// RRR Format
class RRRForm<bits<4> opcode, dag OOL, dag IOL, string asmstr,
              InstrItinClass itin, list<dag> pattern>
        : SPUInstr<OOL, IOL, asmstr, itin>
{
  bits<7> RA;
  bits<7> RB;
  bits<7> RC;
  bits<7> RT;

  let Pattern = pattern;

  let Inst{0-3} = opcode;
  let Inst{4-10} = RT;
  let Inst{11-17} = RB;
  let Inst{18-24} = RA;
  let Inst{25-31} = RC;
}

// RI7 Format
class RI7Form<bits<11> opcode, dag OOL, dag IOL, string asmstr,
              InstrItinClass itin, list<dag> pattern>
        : SPUInstr<OOL, IOL, asmstr, itin>
{
  bits<7> i7;
  bits<7> RA;
  bits<7> RT;

  let Pattern = pattern;

  let Inst{0-10} = opcode;
  let Inst{11-17} = i7;
  let Inst{18-24} = RA;
  let Inst{25-31} = RT;
}

// CVTIntFp Format
class CVTIntFPForm<bits<10> opcode, dag OOL, dag IOL, string asmstr,
                   InstrItinClass itin, list<dag> pattern>
        : SPUInstr<OOL, IOL, asmstr, itin>
{
  bits<7> RA;
  bits<7> RT;

  let Pattern = pattern;

  let Inst{0-9} = opcode;
  let Inst{10-17} = 0;
  let Inst{18-24} = RA;
  let Inst{25-31} = RT;
}

let RA = 0 in {
  class BICondForm<bits<11> opcode, dag OOL, dag IOL, string asmstr, list<dag> pattern>
           : RRForm<opcode, OOL, IOL, asmstr, BranchResolv, pattern>
  { }

  let RT = 0 in {
    // Branch instruction format (without D/E flag settings)
    class BRForm<bits<11> opcode, dag OOL, dag IOL, string asmstr,
               InstrItinClass itin, list<dag> pattern>
          : RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
    { }

    class BIForm<bits<11> opcode, string asmstr, list<dag> pattern>
             : RRForm<opcode, (outs), (ins R32C:$func), asmstr, BranchResolv,
                      pattern>
    { }

    let RB = 0 in {
      // Return instruction (bi, branch indirect), RA is zero (LR):
      class RETForm<string asmstr, list<dag> pattern>
             : BRForm<0b00010101100, (outs), (ins), asmstr, BranchResolv,
                      pattern>
      { }
    }
  }
}

// Branch indirect external data forms:
class BISLEDForm<bits<2> DE_flag, string asmstr, list<dag> pattern>
         : SPUInstr<(outs), (ins indcalltarget:$func), asmstr, BranchResolv>
{
  bits<7> Rcalldest;

  let Pattern = pattern;

  let Inst{0-10} = 0b11010101100;
  let Inst{11} = 0;
  let Inst{12-13} = DE_flag;
  let Inst{14-17} = 0b0000;
  let Inst{18-24} = Rcalldest;
  let Inst{25-31} = 0b0000000;
}

// RI10 Format
class RI10Form<bits<8> opcode, dag OOL, dag IOL, string asmstr,
              InstrItinClass itin, list<dag> pattern>
        : SPUInstr<OOL, IOL, asmstr, itin>
{
  bits<10> i10;
  bits<7> RA;
  bits<7> RT;

  let Pattern = pattern;

  let Inst{0-7} = opcode;
  let Inst{8-17} = i10;
  let Inst{18-24} = RA;
  let Inst{25-31} = RT;
}

// RI10 Format, where the constant is zero (or effectively ignored by the
// SPU)
let i10 = 0 in {
  class RI10Form_1<bits<8> opcode, dag OOL, dag IOL, string asmstr,
                   InstrItinClass itin, list<dag> pattern>
          : RI10Form<opcode, OOL, IOL, asmstr, itin, pattern>
  { }
}

// RI10 Format, where RT is ignored.
// This format is used primarily by the Halt If ... Immediate set of
// instructions
let RT = 0 in {
  class RI10Form_2<bits<8> opcode, dag OOL, dag IOL, string asmstr,
                   InstrItinClass itin, list<dag> pattern>
        : RI10Form<opcode, OOL, IOL, asmstr, itin, pattern>
  { }
}

// RI16 Format
class RI16Form<bits<9> opcode, dag OOL, dag IOL, string asmstr,
              InstrItinClass itin, list<dag> pattern>
        : SPUInstr<OOL, IOL, asmstr, itin>
{
  bits<16> i16;
  bits<7> RT;

  let Pattern = pattern;

  let Inst{0-8} = opcode;
  let Inst{9-24} = i16;
  let Inst{25-31} = RT;
}

// Specialized version of the RI16 Format for unconditional branch relative and
// branch absolute, branch and set link. Note that for branch and set link, the
// link register doesn't have to be $lr, but this is actually hard coded into
// the instruction pattern.

let RT = 0 in {
  class UncondBranch<bits<9> opcode, dag OOL, dag IOL, string asmstr,
                     list<dag> pattern>
    : RI16Form<opcode, OOL, IOL, asmstr, BranchResolv, pattern>
  { }

  class BranchSetLink<bits<9> opcode, dag OOL, dag IOL, string asmstr,
                      list<dag> pattern>
        : RI16Form<opcode, OOL, IOL, asmstr, BranchResolv, pattern>
  { }
}

//===----------------------------------------------------------------------===//
// Specialized versions of RI16:
//===----------------------------------------------------------------------===//

// RI18 Format
class RI18Form<bits<7> opcode, dag OOL, dag IOL, string asmstr,
              InstrItinClass itin, list<dag> pattern>
        : SPUInstr<OOL, IOL, asmstr, itin>
{
  bits<18> i18;
  bits<7> RT;

  let Pattern = pattern;

  let Inst{0-6} = opcode;
  let Inst{7-24} = i18;
  let Inst{25-31} = RT;
}

//===----------------------------------------------------------------------===//
// Instruction formats for intrinsics:
//===----------------------------------------------------------------------===//

// RI10 Format for v8i16 intrinsics
class RI10_Int_v8i16<bits<8> opcode, string opc, InstrItinClass itin,
                     Intrinsic IntID> :
  RI10Form<opcode, (outs VECREG:$rT), (ins s10imm:$val, VECREG:$rA),
           !strconcat(opc, " $rT, $rA, $val"), itin,
           [(set (v8i16 VECREG:$rT), (IntID (v8i16 VECREG:$rA),
                                            i16ImmSExt10:$val))] >;

class RI10_Int_v4i32<bits<8> opcode, string opc, InstrItinClass itin,
                     Intrinsic IntID> :
  RI10Form<opcode, (outs VECREG:$rT), (ins s10imm:$val, VECREG:$rA),
           !strconcat(opc, " $rT, $rA, $val"), itin,
           [(set (v4i32 VECREG:$rT), (IntID (v4i32 VECREG:$rA),
                                            i32ImmSExt10:$val))] >;

// RR Format for v8i16 intrinsics
class RR_Int_v8i16<bits<11> opcode, string opc, InstrItinClass itin,
                   Intrinsic IntID> :
  RRForm<opcode, (outs VECREG:$rT), (ins VECREG:$rA, VECREG:$rB),
         !strconcat(opc, " $rT, $rA, $rB"), itin,
         [(set (v8i16 VECREG:$rT), (IntID (v8i16 VECREG:$rA),
                                          (v8i16 VECREG:$rB)))] >;

// RR Format for v4i32 intrinsics
class RR_Int_v4i32<bits<11> opcode, string opc, InstrItinClass itin,
                   Intrinsic IntID> :
  RRForm<opcode, (outs VECREG:$rT), (ins VECREG:$rA, VECREG:$rB),
         !strconcat(opc, " $rT, $rA, $rB"), itin,
         [(set (v4i32 VECREG:$rT), (IntID (v4i32 VECREG:$rA),
                                          (v4i32 VECREG:$rB)))] >;

//===----------------------------------------------------------------------===//
// Pseudo instructions, like call frames:
//===----------------------------------------------------------------------===//

class Pseudo<dag OOL, dag IOL, string asmstr, list<dag> pattern>
    : SPUInstr<OOL, IOL, asmstr, NoItinerary> {
  let OutOperandList = OOL;
  let InOperandList = IOL;
  let AsmString   = asmstr;
  let Pattern = pattern;
  let Inst{31-0} = 0;
}

//===----------------------------------------------------------------------===//
// Branch hint formats
//===----------------------------------------------------------------------===//
// For hbrr and hbra
class HBI16Form<bits<7> opcode, dag IOL, string asmstr>
        : Instruction {
  field bits<32> Inst;
  bits<16>i16;
  bits<9>RO;

  let Namespace = "SPU";
  let InOperandList = IOL;
  let OutOperandList = (outs); //no output
  let AsmString = asmstr;
  let Itinerary = BranchHints;

  let Inst{0-6} = opcode;
  let Inst{7-8} = RO{8-7};
  let Inst{9-24} = i16;
  let Inst{25-31} = RO{6-0};
}