/*
 * Copyright 2014 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Ben Skeggs <bskeggs@redhat.com>
 */

#include "codegen/nv50_ir_target_gm107.h"

//#define GM107_DEBUG_SCHED_DATA

namespace nv50_ir {

class CodeEmitterGM107 : public CodeEmitter
{
public:
   CodeEmitterGM107(const TargetGM107 *);

   virtual bool emitInstruction(Instruction *);
   virtual uint32_t getMinEncodingSize(const Instruction *) const;

   virtual void prepareEmission(Program *);
   virtual void prepareEmission(Function *);

   inline void setProgramType(Program::Type pType) { progType = pType; }

private:
   const TargetGM107 *targGM107;

   Program::Type progType;

   const Instruction *insn;
   const bool writeIssueDelays;
   uint32_t *data;

private:
   inline void emitField(uint32_t *, int, int, uint32_t);
   inline void emitField(int b, int s, uint32_t v) { emitField(code, b, s, v); }

   inline void emitInsn(uint32_t, bool);
   inline void emitInsn(uint32_t o) { emitInsn(o, true); }
   inline void emitPred();
   inline void emitGPR(int, const Value *);
   inline void emitGPR(int pos) {
      emitGPR(pos, (const Value *)NULL);
   }
   inline void emitGPR(int pos, const ValueRef &ref) {
      emitGPR(pos, ref.get() ? ref.rep() : (const Value *)NULL);
   }
   inline void emitGPR(int pos, const ValueRef *ref) {
      emitGPR(pos, ref ? ref->rep() : (const Value *)NULL);
   }
   inline void emitGPR(int pos, const ValueDef &def) {
      emitGPR(pos, def.get() ? def.rep() : (const Value *)NULL);
   }
   inline void emitSYS(int, const Value *);
   inline void emitSYS(int pos, const ValueRef &ref) {
      emitSYS(pos, ref.get() ? ref.rep() : (const Value *)NULL);
   }
   inline void emitPRED(int, const Value *);
   inline void emitPRED(int pos) {
      emitPRED(pos, (const Value *)NULL);
   }
   inline void emitPRED(int pos, const ValueRef &ref) {
      emitPRED(pos, ref.get() ? ref.rep() : (const Value *)NULL);
   }
   inline void emitPRED(int pos, const ValueDef &def) {
      emitPRED(pos, def.get() ? def.rep() : (const Value *)NULL);
   }
   inline void emitADDR(int, int, int, int, const ValueRef &);
   inline void emitCBUF(int, int, int, int, int, const ValueRef &);
   inline bool longIMMD(const ValueRef &);
   inline void emitIMMD(int, int, const ValueRef &);

   void emitCond3(int, CondCode);
   void emitCond4(int, CondCode);
   void emitCond5(int pos, CondCode cc) { emitCond4(pos, cc); }
   inline void emitO(int);
   inline void emitP(int);
   inline void emitSAT(int);
   inline void emitCC(int);
   inline void emitX(int);
   inline void emitABS(int, const ValueRef &);
   inline void emitNEG(int, const ValueRef &);
   inline void emitNEG2(int, const ValueRef &, const ValueRef &);
   inline void emitFMZ(int, int);
   inline void emitRND(int, RoundMode, int);
   inline void emitRND(int pos) {
      emitRND(pos, insn->rnd, -1);
   }
   inline void emitPDIV(int);
   inline void emitINV(int, const ValueRef &);

   void emitEXIT();
   void emitBRA();
   void emitCAL();
   void emitPCNT();
   void emitCONT();
   void emitPBK();
   void emitBRK();
   void emitPRET();
   void emitRET();
   void emitSSY();
   void emitSYNC();
   void emitSAM();
   void emitRAM();

   void emitMOV();
   void emitS2R();
   void emitF2F();
   void emitF2I();
   void emitI2F();
   void emitI2I();
   void emitSEL();
   void emitSHFL();

   void emitDADD();
   void emitDMUL();
   void emitDFMA();
   void emitDMNMX();
   void emitDSET();
   void emitDSETP();

   void emitFADD();
   void emitFMUL();
   void emitFFMA();
   void emitMUFU();
   void emitFMNMX();
   void emitRRO();
   void emitFCMP();
   void emitFSET();
   void emitFSETP();
   void emitFSWZADD();

   void emitLOP();
   void emitNOT();
   void emitIADD();
   void emitIMUL();
   void emitIMAD();
   void emitISCADD();
   void emitIMNMX();
   void emitICMP();
   void emitISET();
   void emitISETP();
   void emitSHL();
   void emitSHR();
   void emitSHF();
   void emitPOPC();
   void emitBFI();
   void emitBFE();
   void emitFLO();

   void emitLDSTs(int, DataType);
   void emitLDSTc(int);
   void emitLDC();
   void emitLDL();
   void emitLDS();
   void emitLD();
   void emitSTL();
   void emitSTS();
   void emitST();
   void emitALD();
   void emitAST();
   void emitISBERD();
   void emitAL2P();
   void emitIPA();
   void emitATOM();
   void emitATOMS();
   void emitRED();
   void emitCCTL();

   void emitPIXLD();

   void emitTEXs(int);
   void emitTEX();
   void emitTLD();
   void emitTLD4();
   void emitTXD();
   void emitTXQ();
   void emitTMML();
   void emitDEPBAR();

   void emitNOP();
   void emitKIL();
   void emitOUT();

   void emitBAR();
   void emitMEMBAR();

   void emitVOTE();

   void emitSUTarget();
   void emitSUHandle(const int s);
   void emitSUSTx();
   void emitSULDx();
   void emitSUREDx();
};

/*******************************************************************************
 * general instruction layout/fields
 ******************************************************************************/

void
CodeEmitterGM107::emitField(uint32_t *data, int b, int s, uint32_t v)
{
   if (b >= 0) {
      uint32_t m = ((1ULL << s) - 1);
      uint64_t d = (uint64_t)(v & m) << b;
      assert(!(v & ~m) || (v & ~m) == ~m);
      data[1] |= d >> 32;
      data[0] |= d;
   }
}

void
CodeEmitterGM107::emitPred()
{
   if (insn->predSrc >= 0) {
      emitField(16, 3, insn->getSrc(insn->predSrc)->rep()->reg.data.id);
      emitField(19, 1, insn->cc == CC_NOT_P);
   } else {
      emitField(16, 3, 7);
   }
}

void
CodeEmitterGM107::emitInsn(uint32_t hi, bool pred)
{
   code[0] = 0x00000000;
   code[1] = hi;
   if (pred)
      emitPred();
}

void
CodeEmitterGM107::emitGPR(int pos, const Value *val)
{
   emitField(pos, 8, val && !val->inFile(FILE_FLAGS) ?
             val->reg.data.id : 255);
}

void
CodeEmitterGM107::emitSYS(int pos, const Value *val)
{
   int id = val ? val->reg.data.id : -1;

   switch (id) {
   case SV_LANEID         : id = 0x00; break;
   case SV_VERTEX_COUNT   : id = 0x10; break;
   case SV_INVOCATION_ID  : id = 0x11; break;
   case SV_THREAD_KILL    : id = 0x13; break;
   case SV_INVOCATION_INFO: id = 0x1d; break;
   case SV_TID            : id = 0x21 + val->reg.data.sv.index; break;
   case SV_CTAID          : id = 0x25 + val->reg.data.sv.index; break;
   case SV_LANEMASK_EQ    : id = 0x38; break;
   case SV_LANEMASK_LT    : id = 0x39; break;
   case SV_LANEMASK_LE    : id = 0x3a; break;
   case SV_LANEMASK_GT    : id = 0x3b; break;
   case SV_LANEMASK_GE    : id = 0x3c; break;
   case SV_CLOCK          : id = 0x50 + val->reg.data.sv.index; break;
   default:
      assert(!"invalid system value");
      id = 0;
      break;
   }

   emitField(pos, 8, id);
}

void
CodeEmitterGM107::emitPRED(int pos, const Value *val)
{
   emitField(pos, 3, val ? val->reg.data.id : 7);
}

void
CodeEmitterGM107::emitADDR(int gpr, int off, int len, int shr,
                           const ValueRef &ref)
{
   const Value *v = ref.get();
   assert(!(v->reg.data.offset & ((1 << shr) - 1)));
   if (gpr >= 0)
      emitGPR(gpr, ref.getIndirect(0));
   emitField(off, len, v->reg.data.offset >> shr);
}

void
CodeEmitterGM107::emitCBUF(int buf, int gpr, int off, int len, int shr,
                           const ValueRef &ref)
{
   const Value *v = ref.get();
   const Symbol *s = v->asSym();

   assert(!(s->reg.data.offset & ((1 << shr) - 1)));

   emitField(buf,  5, v->reg.fileIndex);
   if (gpr >= 0)
      emitGPR(gpr, ref.getIndirect(0));
   emitField(off, 16, s->reg.data.offset >> shr);
}

bool
CodeEmitterGM107::longIMMD(const ValueRef &ref)
{
   if (ref.getFile() == FILE_IMMEDIATE) {
      const ImmediateValue *imm = ref.get()->asImm();
      if (isFloatType(insn->sType)) {
         if ((imm->reg.data.u32 & 0x00000fff) != 0x00000000)
            return true;
      } else {
         if ((imm->reg.data.u32 & 0xfff00000) != 0x00000000 &&
             (imm->reg.data.u32 & 0xfff00000) != 0xfff00000)
            return true;
      }
   }
   return false;
}

void
CodeEmitterGM107::emitIMMD(int pos, int len, const ValueRef &ref)
{
   const ImmediateValue *imm = ref.get()->asImm();
   uint32_t val = imm->reg.data.u32;

   if (len == 19) {
      if (insn->sType == TYPE_F32 || insn->sType == TYPE_F16) {
         assert(!(val & 0x00000fff));
         val >>= 12;
      } else if (insn->sType == TYPE_F64) {
         assert(!(imm->reg.data.u64 & 0x00000fffffffffffULL));
         val = imm->reg.data.u64 >> 44;
      }
      assert(!(val & 0xfff00000) || (val & 0xfff00000) == 0xfff00000);
      emitField( 56,   1, (val & 0x80000) >> 19);
      emitField(pos, len, (val & 0x7ffff));
   } else {
      emitField(pos, len, val);
   }
}

/*******************************************************************************
 * modifiers
 ******************************************************************************/

void
CodeEmitterGM107::emitCond3(int pos, CondCode code)
{
   int data = 0;

   switch (code) {
   case CC_FL : data = 0x00; break;
   case CC_LTU:
   case CC_LT : data = 0x01; break;
   case CC_EQU:
   case CC_EQ : data = 0x02; break;
   case CC_LEU:
   case CC_LE : data = 0x03; break;
   case CC_GTU:
   case CC_GT : data = 0x04; break;
   case CC_NEU:
   case CC_NE : data = 0x05; break;
   case CC_GEU:
   case CC_GE : data = 0x06; break;
   case CC_TR : data = 0x07; break;
   default:
      assert(!"invalid cond3");
      break;
   }

   emitField(pos, 3, data);
}

void
CodeEmitterGM107::emitCond4(int pos, CondCode code)
{
   int data = 0;

   switch (code) {
   case CC_FL: data = 0x00; break;
   case CC_LT: data = 0x01; break;
   case CC_EQ: data = 0x02; break;
   case CC_LE: data = 0x03; break;
   case CC_GT: data = 0x04; break;
   case CC_NE: data = 0x05; break;
   case CC_GE: data = 0x06; break;
//   case CC_NUM: data = 0x07; break;
//   case CC_NAN: data = 0x08; break;
   case CC_LTU: data = 0x09; break;
   case CC_EQU: data = 0x0a; break;
   case CC_LEU: data = 0x0b; break;
   case CC_GTU: data = 0x0c; break;
   case CC_NEU: data = 0x0d; break;
   case CC_GEU: data = 0x0e; break;
   case CC_TR:  data = 0x0f; break;
   default:
      assert(!"invalid cond4");
      break;
   }

   emitField(pos, 4, data);
}

void
CodeEmitterGM107::emitO(int pos)
{
   emitField(pos, 1, insn->getSrc(0)->reg.file == FILE_SHADER_OUTPUT);
}

void
CodeEmitterGM107::emitP(int pos)
{
   emitField(pos, 1, insn->perPatch);
}

void
CodeEmitterGM107::emitSAT(int pos)
{
   emitField(pos, 1, insn->saturate);
}

void
CodeEmitterGM107::emitCC(int pos)
{
   emitField(pos, 1, insn->flagsDef >= 0);
}

void
CodeEmitterGM107::emitX(int pos)
{
   emitField(pos, 1, insn->flagsSrc >= 0);
}

void
CodeEmitterGM107::emitABS(int pos, const ValueRef &ref)
{
   emitField(pos, 1, ref.mod.abs());
}

void
CodeEmitterGM107::emitNEG(int pos, const ValueRef &ref)
{
   emitField(pos, 1, ref.mod.neg());
}

void
CodeEmitterGM107::emitNEG2(int pos, const ValueRef &a, const ValueRef &b)
{
   emitField(pos, 1, a.mod.neg() ^ b.mod.neg());
}

void
CodeEmitterGM107::emitFMZ(int pos, int len)
{
   emitField(pos, len, insn->dnz << 1 | insn->ftz);
}

void
CodeEmitterGM107::emitRND(int rmp, RoundMode rnd, int rip)
{
   int rm = 0, ri = 0;
   switch (rnd) {
   case ROUND_NI: ri = 1;
   case ROUND_N : rm = 0; break;
   case ROUND_MI: ri = 1;
   case ROUND_M : rm = 1; break;
   case ROUND_PI: ri = 1;
   case ROUND_P : rm = 2; break;
   case ROUND_ZI: ri = 1;
   case ROUND_Z : rm = 3; break;
   default:
      assert(!"invalid round mode");
      break;
   }
   emitField(rip, 1, ri);
   emitField(rmp, 2, rm);
}

void
CodeEmitterGM107::emitPDIV(int pos)
{
   assert(insn->postFactor >= -3 && insn->postFactor <= 3);
   if (insn->postFactor > 0)
      emitField(pos, 3, 7 - insn->postFactor);
   else
      emitField(pos, 3, 0 - insn->postFactor);
}

void
CodeEmitterGM107::emitINV(int pos, const ValueRef &ref)
{
   emitField(pos, 1, !!(ref.mod & Modifier(NV50_IR_MOD_NOT)));
}

/*******************************************************************************
 * control flow
 ******************************************************************************/

void
CodeEmitterGM107::emitEXIT()
{
   emitInsn (0xe3000000);
   emitCond5(0x00, CC_TR);
}

void
CodeEmitterGM107::emitBRA()
{
   const FlowInstruction *insn = this->insn->asFlow();
   int gpr = -1;

   if (insn->indirect) {
      if (insn->absolute)
         emitInsn(0xe2000000); // JMX
      else
         emitInsn(0xe2500000); // BRX
      gpr = 0x08;
   } else {
      if (insn->absolute)
         emitInsn(0xe2100000); // JMP
      else
         emitInsn(0xe2400000); // BRA
      emitField(0x07, 1, insn->allWarp);
   }

   emitField(0x06, 1, insn->limit);
   emitCond5(0x00, CC_TR);

   if (!insn->srcExists(0) || insn->src(0).getFile() != FILE_MEMORY_CONST) {
      int32_t pos = insn->target.bb->binPos;
      if (writeIssueDelays && !(pos & 0x1f))
         pos += 8;
      if (!insn->absolute)
         emitField(0x14, 24, pos - (codeSize + 8));
      else
         emitField(0x14, 32, pos);
   } else {
      emitCBUF (0x24, gpr, 20, 16, 0, insn->src(0));
      emitField(0x05, 1, 1);
   }
}

void
CodeEmitterGM107::emitCAL()
{
   const FlowInstruction *insn = this->insn->asFlow();

   if (insn->absolute) {
      emitInsn(0xe2200000, 0); // JCAL
   } else {
      emitInsn(0xe2600000, 0); // CAL
   }

   if (!insn->srcExists(0) || insn->src(0).getFile() != FILE_MEMORY_CONST) {
      if (!insn->absolute)
         emitField(0x14, 24, insn->target.bb->binPos - (codeSize + 8));
      else {
         if (insn->builtin) {
            int pcAbs = targGM107->getBuiltinOffset(insn->target.builtin);
            addReloc(RelocEntry::TYPE_BUILTIN, 0, pcAbs, 0xfff00000,  20);
            addReloc(RelocEntry::TYPE_BUILTIN, 1, pcAbs, 0x000fffff, -12);
         } else {
            emitField(0x14, 32, insn->target.bb->binPos);
         }
      }
   } else {
      emitCBUF (0x24, -1, 20, 16, 0, insn->src(0));
      emitField(0x05, 1, 1);
   }
}

void
CodeEmitterGM107::emitPCNT()
{
   const FlowInstruction *insn = this->insn->asFlow();

   emitInsn(0xe2b00000, 0);

   if (!insn->srcExists(0) || insn->src(0).getFile() != FILE_MEMORY_CONST) {
      emitField(0x14, 24, insn->target.bb->binPos - (codeSize + 8));
   } else {
      emitCBUF (0x24, -1, 20, 16, 0, insn->src(0));
      emitField(0x05, 1, 1);
   }
}

void
CodeEmitterGM107::emitCONT()
{
   emitInsn (0xe3500000);
   emitCond5(0x00, CC_TR);
}

void
CodeEmitterGM107::emitPBK()
{
   const FlowInstruction *insn = this->insn->asFlow();

   emitInsn(0xe2a00000, 0);

   if (!insn->srcExists(0) || insn->src(0).getFile() != FILE_MEMORY_CONST) {
      emitField(0x14, 24, insn->target.bb->binPos - (codeSize + 8));
   } else {
      emitCBUF (0x24, -1, 20, 16, 0, insn->src(0));
      emitField(0x05, 1, 1);
   }
}

void
CodeEmitterGM107::emitBRK()
{
   emitInsn (0xe3400000);
   emitCond5(0x00, CC_TR);
}

void
CodeEmitterGM107::emitPRET()
{
   const FlowInstruction *insn = this->insn->asFlow();

   emitInsn(0xe2700000, 0);

   if (!insn->srcExists(0) || insn->src(0).getFile() != FILE_MEMORY_CONST) {
      emitField(0x14, 24, insn->target.bb->binPos - (codeSize + 8));
   } else {
      emitCBUF (0x24, -1, 20, 16, 0, insn->src(0));
      emitField(0x05, 1, 1);
   }
}

void
CodeEmitterGM107::emitRET()
{
   emitInsn (0xe3200000);
   emitCond5(0x00, CC_TR);
}

void
CodeEmitterGM107::emitSSY()
{
   const FlowInstruction *insn = this->insn->asFlow();

   emitInsn(0xe2900000, 0);

   if (!insn->srcExists(0) || insn->src(0).getFile() != FILE_MEMORY_CONST) {
      emitField(0x14, 24, insn->target.bb->binPos - (codeSize + 8));
   } else {
      emitCBUF (0x24, -1, 20, 16, 0, insn->src(0));
      emitField(0x05, 1, 1);
   }
}

void
CodeEmitterGM107::emitSYNC()
{
   emitInsn (0xf0f80000);
   emitCond5(0x00, CC_TR);
}

void
CodeEmitterGM107::emitSAM()
{
   emitInsn(0xe3700000, 0);
}

void
CodeEmitterGM107::emitRAM()
{
   emitInsn(0xe3800000, 0);
}

/*******************************************************************************
 * predicate/cc
 ******************************************************************************/

/*******************************************************************************
 * movement / conversion
 ******************************************************************************/

void
CodeEmitterGM107::emitMOV()
{
   if (insn->src(0).getFile() != FILE_IMMEDIATE) {
      switch (insn->src(0).getFile()) {
      case FILE_GPR:
         if (insn->def(0).getFile() == FILE_PREDICATE) {
            emitInsn(0x5b6a0000);
            emitGPR (0x08);
         } else {
            emitInsn(0x5c980000);
         }
         emitGPR (0x14, insn->src(0));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4c980000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x38980000);
         emitIMMD(0x14, 19, insn->src(0));
         break;
      case FILE_PREDICATE:
         emitInsn(0x50880000);
         emitPRED(0x0c, insn->src(0));
         emitPRED(0x1d);
         emitPRED(0x27);
         break;
      default:
         assert(!"bad src file");
         break;
      }
      if (insn->def(0).getFile() != FILE_PREDICATE &&
          insn->src(0).getFile() != FILE_PREDICATE)
         emitField(0x27, 4, insn->lanes);
   } else {
      emitInsn (0x01000000);
      emitIMMD (0x14, 32, insn->src(0));
      emitField(0x0c, 4, insn->lanes);
   }

   if (insn->def(0).getFile() == FILE_PREDICATE) {
      emitPRED(0x27);
      emitPRED(0x03, insn->def(0));
      emitPRED(0x00);
   } else {
      emitGPR(0x00, insn->def(0));
   }
}

void
CodeEmitterGM107::emitS2R()
{
   emitInsn(0xf0c80000);
   emitSYS (0x14, insn->src(0));
   emitGPR (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitF2F()
{
   RoundMode rnd = insn->rnd;

   switch (insn->op) {
   case OP_FLOOR: rnd = ROUND_MI; break;
   case OP_CEIL : rnd = ROUND_PI; break;
   case OP_TRUNC: rnd = ROUND_ZI; break;
   default:
      break;
   }

   switch (insn->src(0).getFile()) {
   case FILE_GPR:
      emitInsn(0x5ca80000);
      emitGPR (0x14, insn->src(0));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4ca80000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38a80000);
      emitIMMD(0x14, 19, insn->src(0));
      break;
   default:
      assert(!"bad src0 file");
      break;
   }

   emitField(0x32, 1, (insn->op == OP_SAT) || insn->saturate);
   emitField(0x31, 1, (insn->op == OP_ABS) || insn->src(0).mod.abs());
   emitCC   (0x2f);
   emitField(0x2d, 1, (insn->op == OP_NEG) || insn->src(0).mod.neg());
   emitFMZ  (0x2c, 1);
   emitField(0x29, 1, insn->subOp);
   emitRND  (0x27, rnd, 0x2a);
   emitField(0x0a, 2, util_logbase2(typeSizeof(insn->sType)));
   emitField(0x08, 2, util_logbase2(typeSizeof(insn->dType)));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitF2I()
{
   RoundMode rnd = insn->rnd;

   switch (insn->op) {
   case OP_FLOOR: rnd = ROUND_M; break;
   case OP_CEIL : rnd = ROUND_P; break;
   case OP_TRUNC: rnd = ROUND_Z; break;
   default:
      break;
   }

   switch (insn->src(0).getFile()) {
   case FILE_GPR:
      emitInsn(0x5cb00000);
      emitGPR (0x14, insn->src(0));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4cb00000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38b00000);
      emitIMMD(0x14, 19, insn->src(0));
      break;
   default:
      assert(!"bad src0 file");
      break;
   }

   emitField(0x31, 1, (insn->op == OP_ABS) || insn->src(0).mod.abs());
   emitCC   (0x2f);
   emitField(0x2d, 1, (insn->op == OP_NEG) || insn->src(0).mod.neg());
   emitFMZ  (0x2c, 1);
   emitRND  (0x27, rnd, 0x2a);
   emitField(0x0c, 1, isSignedType(insn->dType));
   emitField(0x0a, 2, util_logbase2(typeSizeof(insn->sType)));
   emitField(0x08, 2, util_logbase2(typeSizeof(insn->dType)));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitI2F()
{
   RoundMode rnd = insn->rnd;

   switch (insn->op) {
   case OP_FLOOR: rnd = ROUND_M; break;
   case OP_CEIL : rnd = ROUND_P; break;
   case OP_TRUNC: rnd = ROUND_Z; break;
   default:
      break;
   }

   switch (insn->src(0).getFile()) {
   case FILE_GPR:
      emitInsn(0x5cb80000);
      emitGPR (0x14, insn->src(0));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4cb80000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38b80000);
      emitIMMD(0x14, 19, insn->src(0));
      break;
   default:
      assert(!"bad src0 file");
      break;
   }

   emitField(0x31, 1, (insn->op == OP_ABS) || insn->src(0).mod.abs());
   emitCC   (0x2f);
   emitField(0x2d, 1, (insn->op == OP_NEG) || insn->src(0).mod.neg());
   emitField(0x29, 2, insn->subOp);
   emitRND  (0x27, rnd, -1);
   emitField(0x0d, 1, isSignedType(insn->sType));
   emitField(0x0a, 2, util_logbase2(typeSizeof(insn->sType)));
   emitField(0x08, 2, util_logbase2(typeSizeof(insn->dType)));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitI2I()
{
   switch (insn->src(0).getFile()) {
   case FILE_GPR:
      emitInsn(0x5ce00000);
      emitGPR (0x14, insn->src(0));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4ce00000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38e00000);
      emitIMMD(0x14, 19, insn->src(0));
      break;
   default:
      assert(!"bad src0 file");
      break;
   }

   emitSAT  (0x32);
   emitField(0x31, 1, (insn->op == OP_ABS) || insn->src(0).mod.abs());
   emitCC   (0x2f);
   emitField(0x2d, 1, (insn->op == OP_NEG) || insn->src(0).mod.neg());
   emitField(0x29, 2, insn->subOp);
   emitField(0x0d, 1, isSignedType(insn->sType));
   emitField(0x0c, 1, isSignedType(insn->dType));
   emitField(0x0a, 2, util_logbase2(typeSizeof(insn->sType)));
   emitField(0x08, 2, util_logbase2(typeSizeof(insn->dType)));
   emitGPR  (0x00, insn->def(0));
}

static void
selpFlip(const FixupEntry *entry, uint32_t *code, const FixupData& data)
{
   int loc = entry->loc;
   if (data.force_persample_interp)
      code[loc + 1] |= 1 << 10;
   else
      code[loc + 1] &= ~(1 << 10);
}

void
CodeEmitterGM107::emitSEL()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5ca00000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4ca00000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38a00000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitINV (0x2a, insn->src(2));
   emitPRED(0x27, insn->src(2));
   emitGPR (0x08, insn->src(0));
   emitGPR (0x00, insn->def(0));

   if (insn->subOp == 1) {
      addInterp(0, 0, selpFlip);
   }
}

void
CodeEmitterGM107::emitSHFL()
{
   int type = 0;

   emitInsn (0xef100000);

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitGPR(0x14, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitIMMD(0x14, 5, insn->src(1));
      type |= 1;
      break;
   default:
      assert(!"invalid src1 file");
      break;
   }

   switch (insn->src(2).getFile()) {
   case FILE_GPR:
      emitGPR(0x27, insn->src(2));
      break;
   case FILE_IMMEDIATE:
      emitIMMD(0x22, 13, insn->src(2));
      type |= 2;
      break;
   default:
      assert(!"invalid src2 file");
      break;
   }

   if (!insn->defExists(1))
      emitPRED(0x30);
   else {
      assert(insn->def(1).getFile() == FILE_PREDICATE);
      emitPRED(0x30, insn->def(1));
   }

   emitField(0x1e, 2, insn->subOp);
   emitField(0x1c, 2, type);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

/*******************************************************************************
 * double
 ******************************************************************************/

void
CodeEmitterGM107::emitDADD()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c700000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c700000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38700000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }
   emitABS(0x31, insn->src(1));
   emitNEG(0x30, insn->src(0));
   emitCC (0x2f);
   emitABS(0x2e, insn->src(0));
   emitNEG(0x2d, insn->src(1));

   if (insn->op == OP_SUB)
      code[1] ^= 0x00002000;

   emitGPR(0x08, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitDMUL()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c800000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c800000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38800000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitNEG2(0x30, insn->src(0), insn->src(1));
   emitCC  (0x2f);
   emitRND (0x27);
   emitGPR (0x08, insn->src(0));
   emitGPR (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitDFMA()
{
   switch(insn->src(2).getFile()) {
   case FILE_GPR:
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5b700000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4b700000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x36700000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitGPR (0x27, insn->src(2));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x53700000);
      emitGPR (0x27, insn->src(1));
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(2));
      break;
   default:
      assert(!"bad src2 file");
      break;
   }

   emitRND (0x32);
   emitNEG (0x31, insn->src(2));
   emitNEG2(0x30, insn->src(0), insn->src(1));
   emitCC  (0x2f);
   emitGPR (0x08, insn->src(0));
   emitGPR (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitDMNMX()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c500000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c500000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38500000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitABS  (0x31, insn->src(1));
   emitNEG  (0x30, insn->src(0));
   emitCC   (0x2f);
   emitABS  (0x2e, insn->src(0));
   emitNEG  (0x2d, insn->src(1));
   emitField(0x2a, 1, insn->op == OP_MAX);
   emitPRED (0x27);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitDSET()
{
   const CmpInstruction *insn = this->insn->asCmp();

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x59000000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x49000000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x32000000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   if (insn->op != OP_SET) {
      switch (insn->op) {
      case OP_SET_AND: emitField(0x2d, 2, 0); break;
      case OP_SET_OR : emitField(0x2d, 2, 1); break;
      case OP_SET_XOR: emitField(0x2d, 2, 2); break;
      default:
         assert(!"invalid set op");
         break;
      }
      emitPRED(0x27, insn->src(2));
   } else {
      emitPRED(0x27);
   }

   emitABS  (0x36, insn->src(0));
   emitNEG  (0x35, insn->src(1));
   emitField(0x34, 1, insn->dType == TYPE_F32);
   emitCond4(0x30, insn->setCond);
   emitCC   (0x2f);
   emitABS  (0x2c, insn->src(1));
   emitNEG  (0x2b, insn->src(0));
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitDSETP()
{
   const CmpInstruction *insn = this->insn->asCmp();

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5b800000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4b800000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x36800000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   if (insn->op != OP_SET) {
      switch (insn->op) {
      case OP_SET_AND: emitField(0x2d, 2, 0); break;
      case OP_SET_OR : emitField(0x2d, 2, 1); break;
      case OP_SET_XOR: emitField(0x2d, 2, 2); break;
      default:
         assert(!"invalid set op");
         break;
      }
      emitPRED(0x27, insn->src(2));
   } else {
      emitPRED(0x27);
   }

   emitCond4(0x30, insn->setCond);
   emitABS  (0x2c, insn->src(1));
   emitNEG  (0x2b, insn->src(0));
   emitGPR  (0x08, insn->src(0));
   emitABS  (0x07, insn->src(0));
   emitNEG  (0x06, insn->src(1));
   emitPRED (0x03, insn->def(0));
   if (insn->defExists(1))
      emitPRED(0x00, insn->def(1));
   else
      emitPRED(0x00);
}

/*******************************************************************************
 * float
 ******************************************************************************/

void
CodeEmitterGM107::emitFADD()
{
   if (!longIMMD(insn->src(1))) {
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5c580000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4c580000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x38580000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitSAT(0x32);
      emitABS(0x31, insn->src(1));
      emitNEG(0x30, insn->src(0));
      emitCC (0x2f);
      emitABS(0x2e, insn->src(0));
      emitNEG(0x2d, insn->src(1));
      emitFMZ(0x2c, 1);

      if (insn->op == OP_SUB)
         code[1] ^= 0x00002000;
   } else {
      emitInsn(0x08000000);
      emitABS(0x39, insn->src(1));
      emitNEG(0x38, insn->src(0));
      emitFMZ(0x37, 1);
      emitABS(0x36, insn->src(0));
      emitNEG(0x35, insn->src(1));
      emitCC  (0x34);
      emitIMMD(0x14, 32, insn->src(1));

      if (insn->op == OP_SUB)
         code[1] ^= 0x00080000;
   }

   emitGPR(0x08, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitFMUL()
{
   if (!longIMMD(insn->src(1))) {
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5c680000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4c680000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x38680000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitSAT (0x32);
      emitNEG2(0x30, insn->src(0), insn->src(1));
      emitCC  (0x2f);
      emitFMZ (0x2c, 2);
      emitPDIV(0x29);
      emitRND (0x27);
   } else {
      emitInsn(0x1e000000);
      emitSAT (0x37);
      emitFMZ (0x35, 2);
      emitCC  (0x34);
      emitIMMD(0x14, 32, insn->src(1));
      if (insn->src(0).mod.neg() ^ insn->src(1).mod.neg())
         code[1] ^= 0x00080000; /* flip immd sign bit */
   }

   emitGPR(0x08, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitFFMA()
{
   bool isLongIMMD = false;
   switch(insn->src(2).getFile()) {
   case FILE_GPR:
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x59800000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x49800000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         if (longIMMD(insn->getSrc(1))) {
            assert(insn->getDef(0)->reg.data.id == insn->getSrc(2)->reg.data.id);
            isLongIMMD = true;
            emitInsn(0x0c000000);
            emitIMMD(0x14, 32, insn->src(1));
         } else {
            emitInsn(0x32800000);
            emitIMMD(0x14, 19, insn->src(1));
         }
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      if (!isLongIMMD)
         emitGPR (0x27, insn->src(2));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x51800000);
      emitGPR (0x27, insn->src(1));
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(2));
      break;
   default:
      assert(!"bad src2 file");
      break;
   }

   if (isLongIMMD) {
      emitNEG (0x39, insn->src(2));
      emitNEG2(0x38, insn->src(0), insn->src(1));
      emitSAT (0x37);
      emitCC  (0x34);
   } else {
      emitRND (0x33);
      emitSAT (0x32);
      emitNEG (0x31, insn->src(2));
      emitNEG2(0x30, insn->src(0), insn->src(1));
      emitCC  (0x2f);
   }

   emitFMZ(0x35, 2);
   emitGPR(0x08, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitMUFU()
{
   int mufu = 0;

   switch (insn->op) {
   case OP_COS: mufu = 0; break;
   case OP_SIN: mufu = 1; break;
   case OP_EX2: mufu = 2; break;
   case OP_LG2: mufu = 3; break;
   case OP_RCP: mufu = 4 + 2 * insn->subOp; break;
   case OP_RSQ: mufu = 5 + 2 * insn->subOp; break;
   default:
      assert(!"invalid mufu");
      break;
   }

   emitInsn (0x50800000);
   emitSAT  (0x32);
   emitNEG  (0x30, insn->src(0));
   emitABS  (0x2e, insn->src(0));
   emitField(0x14, 3, mufu);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitFMNMX()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c600000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c600000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38600000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitField(0x2a, 1, insn->op == OP_MAX);
   emitPRED (0x27);

   emitABS(0x31, insn->src(1));
   emitNEG(0x30, insn->src(0));
   emitCC (0x2f);
   emitABS(0x2e, insn->src(0));
   emitNEG(0x2d, insn->src(1));
   emitFMZ(0x2c, 1);
   emitGPR(0x08, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitRRO()
{
   switch (insn->src(0).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c900000);
      emitGPR (0x14, insn->src(0));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c900000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38900000);
      emitIMMD(0x14, 19, insn->src(0));
      break;
   default:
      assert(!"bad src file");
      break;
   }

   emitABS  (0x31, insn->src(0));
   emitNEG  (0x2d, insn->src(0));
   emitField(0x27, 1, insn->op == OP_PREEX2);
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitFCMP()
{
   const CmpInstruction *insn = this->insn->asCmp();
   CondCode cc = insn->setCond;

   if (insn->src(2).mod.neg())
      cc = reverseCondCode(cc);

   switch(insn->src(2).getFile()) {
   case FILE_GPR:
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5ba00000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4ba00000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x36a00000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitGPR (0x27, insn->src(2));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x53a00000);
      emitGPR (0x27, insn->src(1));
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(2));
      break;
   default:
      assert(!"bad src2 file");
      break;
   }

   emitCond4(0x30, cc);
   emitFMZ  (0x2f, 1);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitFSET()
{
   const CmpInstruction *insn = this->insn->asCmp();

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x58000000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x48000000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x30000000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   if (insn->op != OP_SET) {
      switch (insn->op) {
      case OP_SET_AND: emitField(0x2d, 2, 0); break;
      case OP_SET_OR : emitField(0x2d, 2, 1); break;
      case OP_SET_XOR: emitField(0x2d, 2, 2); break;
      default:
         assert(!"invalid set op");
         break;
      }
      emitPRED(0x27, insn->src(2));
   } else {
      emitPRED(0x27);
   }

   emitFMZ  (0x37, 1);
   emitABS  (0x36, insn->src(0));
   emitNEG  (0x35, insn->src(1));
   emitField(0x34, 1, insn->dType == TYPE_F32);
   emitCond4(0x30, insn->setCond);
   emitCC   (0x2f);
   emitABS  (0x2c, insn->src(1));
   emitNEG  (0x2b, insn->src(0));
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitFSETP()
{
   const CmpInstruction *insn = this->insn->asCmp();

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5bb00000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4bb00000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x36b00000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   if (insn->op != OP_SET) {
      switch (insn->op) {
      case OP_SET_AND: emitField(0x2d, 2, 0); break;
      case OP_SET_OR : emitField(0x2d, 2, 1); break;
      case OP_SET_XOR: emitField(0x2d, 2, 2); break;
      default:
         assert(!"invalid set op");
         break;
      }
      emitPRED(0x27, insn->src(2));
   } else {
      emitPRED(0x27);
   }

   emitCond4(0x30, insn->setCond);
   emitFMZ  (0x2f, 1);
   emitABS  (0x2c, insn->src(1));
   emitNEG  (0x2b, insn->src(0));
   emitGPR  (0x08, insn->src(0));
   emitABS  (0x07, insn->src(0));
   emitNEG  (0x06, insn->src(1));
   emitPRED (0x03, insn->def(0));
   if (insn->defExists(1))
      emitPRED(0x00, insn->def(1));
   else
      emitPRED(0x00);
}

void
CodeEmitterGM107::emitFSWZADD()
{
   emitInsn (0x50f80000);
   emitCC   (0x2f);
   emitFMZ  (0x2c, 1);
   emitRND  (0x27);
   emitField(0x26, 1, insn->lanes); /* abused for .ndv */
   emitField(0x1c, 8, insn->subOp);
   if (insn->predSrc != 1)
      emitGPR  (0x14, insn->src(1));
   else
      emitGPR  (0x14);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

/*******************************************************************************
 * integer
 ******************************************************************************/

void
CodeEmitterGM107::emitLOP()
{
   int lop = 0;

   switch (insn->op) {
   case OP_AND: lop = 0; break;
   case OP_OR : lop = 1; break;
   case OP_XOR: lop = 2; break;
   default:
      assert(!"invalid lop");
      break;
   }

   if (insn->src(1).getFile() != FILE_IMMEDIATE) {
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5c400000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4c400000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x38400000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitPRED (0x30);
      emitCC   (0x2f);
      emitX    (0x2b);
      emitField(0x29, 2, lop);
      emitINV  (0x28, insn->src(1));
      emitINV  (0x27, insn->src(0));
   } else {
      emitInsn (0x04000000);
      emitX    (0x39);
      emitINV  (0x38, insn->src(1));
      emitINV  (0x37, insn->src(0));
      emitField(0x35, 2, lop);
      emitCC   (0x34);
      emitIMMD (0x14, 32, insn->src(1));
   }

   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

/* special-case of emitLOP(): lop pass_b dst 0 ~src */
void
CodeEmitterGM107::emitNOT()
{
   if (!longIMMD(insn->src(0))) {
      switch (insn->src(0).getFile()) {
      case FILE_GPR:
         emitInsn(0x5c400700);
         emitGPR (0x14, insn->src(0));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4c400700);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x38400700);
         emitIMMD(0x14, 19, insn->src(0));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitPRED (0x30);
   } else {
      emitInsn (0x05600000);
      emitIMMD (0x14, 32, insn->src(1));
   }

   emitGPR(0x08);
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitIADD()
{
   if (insn->src(1).getFile() != FILE_IMMEDIATE) {
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5c100000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4c100000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x38100000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitSAT(0x32);
      emitNEG(0x31, insn->src(0));
      emitNEG(0x30, insn->src(1));
      emitCC (0x2f);
      emitX  (0x2b);
   } else {
      emitInsn(0x1c000000);
      emitNEG (0x38, insn->src(0));
      emitSAT (0x36);
      emitX   (0x35);
      emitCC  (0x34);
      emitIMMD(0x14, 32, insn->src(1));
   }

   if (insn->op == OP_SUB)
      code[1] ^= 0x00010000;

   emitGPR(0x08, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitIMUL()
{
   if (insn->src(1).getFile() != FILE_IMMEDIATE) {
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5c380000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4c380000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x38380000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitCC   (0x2f);
      emitField(0x29, 1, isSignedType(insn->sType));
      emitField(0x28, 1, isSignedType(insn->dType));
      emitField(0x27, 1, insn->subOp == NV50_IR_SUBOP_MUL_HIGH);
   } else {
      emitInsn (0x1f000000);
      emitField(0x37, 1, isSignedType(insn->sType));
      emitField(0x36, 1, isSignedType(insn->dType));
      emitField(0x35, 1, insn->subOp == NV50_IR_SUBOP_MUL_HIGH);
      emitCC   (0x34);
      emitIMMD (0x14, 32, insn->src(1));
   }

   emitGPR(0x08, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitIMAD()
{
   /*XXX: imad32i exists, but not using it as third src overlaps dst */
   switch(insn->src(2).getFile()) {
   case FILE_GPR:
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5a000000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4a000000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x34000000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitGPR (0x27, insn->src(2));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x52000000);
      emitGPR (0x27, insn->src(1));
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(2));
      break;
   default:
      assert(!"bad src2 file");
      break;
   }

   emitField(0x36, 1, insn->subOp == NV50_IR_SUBOP_MUL_HIGH);
   emitField(0x35, 1, isSignedType(insn->sType));
   emitNEG  (0x34, insn->src(2));
   emitNEG2 (0x33, insn->src(0), insn->src(1));
   emitSAT  (0x32);
   emitX    (0x31);
   emitField(0x30, 1, isSignedType(insn->dType));
   emitCC   (0x2f);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitISCADD()
{
   assert(insn->src(1).get()->asImm());

   switch (insn->src(2).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c180000);
      emitGPR (0x14, insn->src(2));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c180000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(2));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38180000);
      emitIMMD(0x14, 19, insn->src(2));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }
   emitNEG (0x31, insn->src(0));
   emitNEG (0x30, insn->src(2));
   emitCC  (0x2f);
   emitIMMD(0x27, 5, insn->src(1));
   emitGPR (0x08, insn->src(0));
   emitGPR (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitIMNMX()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c200000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c200000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38200000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitField(0x30, 1, isSignedType(insn->dType));
   emitCC   (0x2f);
   emitField(0x2b, 2, insn->subOp);
   emitField(0x2a, 1, insn->op == OP_MAX);
   emitPRED (0x27);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitICMP()
{
   const CmpInstruction *insn = this->insn->asCmp();
   CondCode cc = insn->setCond;

   if (insn->src(2).mod.neg())
      cc = reverseCondCode(cc);

   switch(insn->src(2).getFile()) {
   case FILE_GPR:
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5b400000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4b400000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x36400000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitGPR (0x27, insn->src(2));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x53400000);
      emitGPR (0x27, insn->src(1));
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(2));
      break;
   default:
      assert(!"bad src2 file");
      break;
   }

   emitCond3(0x31, cc);
   emitField(0x30, 1, isSignedType(insn->sType));
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitISET()
{
   const CmpInstruction *insn = this->insn->asCmp();

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5b500000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4b500000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x36500000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   if (insn->op != OP_SET) {
      switch (insn->op) {
      case OP_SET_AND: emitField(0x2d, 2, 0); break;
      case OP_SET_OR : emitField(0x2d, 2, 1); break;
      case OP_SET_XOR: emitField(0x2d, 2, 2); break;
      default:
         assert(!"invalid set op");
         break;
      }
      emitPRED(0x27, insn->src(2));
   } else {
      emitPRED(0x27);
   }

   emitCond3(0x31, insn->setCond);
   emitField(0x30, 1, isSignedType(insn->sType));
   emitCC   (0x2f);
   emitField(0x2c, 1, insn->dType == TYPE_F32);
   emitX    (0x2b);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitISETP()
{
   const CmpInstruction *insn = this->insn->asCmp();

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5b600000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4b600000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x36600000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   if (insn->op != OP_SET) {
      switch (insn->op) {
      case OP_SET_AND: emitField(0x2d, 2, 0); break;
      case OP_SET_OR : emitField(0x2d, 2, 1); break;
      case OP_SET_XOR: emitField(0x2d, 2, 2); break;
      default:
         assert(!"invalid set op");
         break;
      }
      emitPRED(0x27, insn->src(2));
   } else {
      emitPRED(0x27);
   }

   emitCond3(0x31, insn->setCond);
   emitField(0x30, 1, isSignedType(insn->sType));
   emitX    (0x2b);
   emitGPR  (0x08, insn->src(0));
   emitPRED (0x03, insn->def(0));
   if (insn->defExists(1))
      emitPRED(0x00, insn->def(1));
   else
      emitPRED(0x00);
}

void
CodeEmitterGM107::emitSHL()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c480000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c480000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38480000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitCC   (0x2f);
   emitX    (0x2b);
   emitField(0x27, 1, insn->subOp == NV50_IR_SUBOP_SHIFT_WRAP);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitSHR()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c280000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c280000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38280000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitField(0x30, 1, isSignedType(insn->dType));
   emitCC   (0x2f);
   emitX    (0x2c);
   emitField(0x27, 1, insn->subOp == NV50_IR_SUBOP_SHIFT_WRAP);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitSHF()
{
   unsigned type;

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(insn->op == OP_SHL ? 0x5bf80000 : 0x5cf80000);
      emitGPR(0x14, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(insn->op == OP_SHL ? 0x36f80000 : 0x38f80000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   switch (insn->sType) {
   case TYPE_U64:
      type = 2;
      break;
   case TYPE_S64:
      type = 3;
      break;
   default:
      type = 0;
      break;
   }

   emitField(0x32, 1, !!(insn->subOp & NV50_IR_SUBOP_SHIFT_WRAP));
   emitX    (0x31);
   emitField(0x30, 1, !!(insn->subOp & NV50_IR_SUBOP_SHIFT_HIGH));
   emitCC   (0x2f);
   emitGPR  (0x27, insn->src(2));
   emitField(0x25, 2, type);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitPOPC()
{
   switch (insn->src(0).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c080000);
      emitGPR (0x14, insn->src(0));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c080000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38080000);
      emitIMMD(0x14, 19, insn->src(0));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitINV(0x28, insn->src(0));
   emitGPR(0x00, insn->def(0));
}

void
CodeEmitterGM107::emitBFI()
{
   switch(insn->src(2).getFile()) {
   case FILE_GPR:
      switch (insn->src(1).getFile()) {
      case FILE_GPR:
         emitInsn(0x5bf00000);
         emitGPR (0x14, insn->src(1));
         break;
      case FILE_MEMORY_CONST:
         emitInsn(0x4bf00000);
         emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
         break;
      case FILE_IMMEDIATE:
         emitInsn(0x36f00000);
         emitIMMD(0x14, 19, insn->src(1));
         break;
      default:
         assert(!"bad src1 file");
         break;
      }
      emitGPR (0x27, insn->src(2));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x53f00000);
      emitGPR (0x27, insn->src(1));
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(2));
      break;
   default:
      assert(!"bad src2 file");
      break;
   }

   emitCC   (0x2f);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitBFE()
{
   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c000000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c000000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38000000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitField(0x30, 1, isSignedType(insn->dType));
   emitCC   (0x2f);
   emitField(0x28, 1, insn->subOp == NV50_IR_SUBOP_EXTBF_REV);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitFLO()
{
   switch (insn->src(0).getFile()) {
   case FILE_GPR:
      emitInsn(0x5c300000);
      emitGPR (0x14, insn->src(0));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0x4c300000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0x38300000);
      emitIMMD(0x14, 19, insn->src(0));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitField(0x30, 1, isSignedType(insn->dType));
   emitCC   (0x2f);
   emitField(0x29, 1, insn->subOp == NV50_IR_SUBOP_BFIND_SAMT);
   emitINV  (0x28, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

/*******************************************************************************
 * memory
 ******************************************************************************/

void
CodeEmitterGM107::emitLDSTs(int pos, DataType type)
{
   int data = 0;

   switch (typeSizeof(type)) {
   case  1: data = isSignedType(type) ? 1 : 0; break;
   case  2: data = isSignedType(type) ? 3 : 2; break;
   case  4: data = 4; break;
   case  8: data = 5; break;
   case 16: data = 6; break;
   default:
      assert(!"bad type");
      break;
   }

   emitField(pos, 3, data);
}

void
CodeEmitterGM107::emitLDSTc(int pos)
{
   int mode = 0;

   switch (insn->cache) {
   case CACHE_CA: mode = 0; break;
   case CACHE_CG: mode = 1; break;
   case CACHE_CS: mode = 2; break;
   case CACHE_CV: mode = 3; break;
   default:
      assert(!"invalid caching mode");
      break;
   }

   emitField(pos, 2, mode);
}

void
CodeEmitterGM107::emitLDC()
{
   emitInsn (0xef900000);
   emitLDSTs(0x30, insn->dType);
   emitField(0x2c, 2, insn->subOp);
   emitCBUF (0x24, 0x08, 0x14, 16, 0, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitLDL()
{
   emitInsn (0xef400000);
   emitLDSTs(0x30, insn->dType);
   emitLDSTc(0x2c);
   emitADDR (0x08, 0x14, 24, 0, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitLDS()
{
   emitInsn (0xef480000);
   emitLDSTs(0x30, insn->dType);
   emitADDR (0x08, 0x14, 24, 0, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitLD()
{
   emitInsn (0x80000000);
   emitPRED (0x3a);
   emitLDSTc(0x38);
   emitLDSTs(0x35, insn->dType);
   emitField(0x34, 1, insn->src(0).getIndirect(0)->getSize() == 8);
   emitADDR (0x08, 0x14, 32, 0, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitSTL()
{
   emitInsn (0xef500000);
   emitLDSTs(0x30, insn->dType);
   emitLDSTc(0x2c);
   emitADDR (0x08, 0x14, 24, 0, insn->src(0));
   emitGPR  (0x00, insn->src(1));
}

void
CodeEmitterGM107::emitSTS()
{
   emitInsn (0xef580000);
   emitLDSTs(0x30, insn->dType);
   emitADDR (0x08, 0x14, 24, 0, insn->src(0));
   emitGPR  (0x00, insn->src(1));
}

void
CodeEmitterGM107::emitST()
{
   emitInsn (0xa0000000);
   emitPRED (0x3a);
   emitLDSTc(0x38);
   emitLDSTs(0x35, insn->dType);
   emitField(0x34, 1, insn->src(0).getIndirect(0)->getSize() == 8);
   emitADDR (0x08, 0x14, 32, 0, insn->src(0));
   emitGPR  (0x00, insn->src(1));
}

void
CodeEmitterGM107::emitALD()
{
   emitInsn (0xefd80000);
   emitField(0x2f, 2, (insn->getDef(0)->reg.size / 4) - 1);
   emitGPR  (0x27, insn->src(0).getIndirect(1));
   emitO    (0x20);
   emitP    (0x1f);
   emitADDR (0x08, 20, 10, 0, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitAST()
{
   emitInsn (0xeff00000);
   emitField(0x2f, 2, (typeSizeof(insn->dType) / 4) - 1);
   emitGPR  (0x27, insn->src(0).getIndirect(1));
   emitP    (0x1f);
   emitADDR (0x08, 20, 10, 0, insn->src(0));
   emitGPR  (0x00, insn->src(1));
}

void
CodeEmitterGM107::emitISBERD()
{
   emitInsn(0xefd00000);
   emitGPR (0x08, insn->src(0));
   emitGPR (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitAL2P()
{
   emitInsn (0xefa00000);
   emitField(0x2f, 2, (insn->getDef(0)->reg.size / 4) - 1);
   emitPRED (0x2c);
   emitO    (0x20);
   emitField(0x14, 11, insn->src(0).get()->reg.data.offset);
   emitGPR  (0x08, insn->src(0).getIndirect(0));
   emitGPR  (0x00, insn->def(0));
}

static void
interpApply(const FixupEntry *entry, uint32_t *code, const FixupData& data)
{
   int ipa = entry->ipa;
   int reg = entry->reg;
   int loc = entry->loc;

   if (data.flatshade &&
       (ipa & NV50_IR_INTERP_MODE_MASK) == NV50_IR_INTERP_SC) {
      ipa = NV50_IR_INTERP_FLAT;
      reg = 0xff;
   } else if (data.force_persample_interp &&
              (ipa & NV50_IR_INTERP_SAMPLE_MASK) == NV50_IR_INTERP_DEFAULT &&
              (ipa & NV50_IR_INTERP_MODE_MASK) != NV50_IR_INTERP_FLAT) {
      ipa |= NV50_IR_INTERP_CENTROID;
   }
   code[loc + 1] &= ~(0xf << 0x14);
   code[loc + 1] |= (ipa & 0x3) << 0x16;
   code[loc + 1] |= (ipa & 0xc) << (0x14 - 2);
   code[loc + 0] &= ~(0xff << 0x14);
   code[loc + 0] |= reg << 0x14;
}

void
CodeEmitterGM107::emitIPA()
{
   int ipam = 0, ipas = 0;

   switch (insn->getInterpMode()) {
   case NV50_IR_INTERP_LINEAR     : ipam = 0; break;
   case NV50_IR_INTERP_PERSPECTIVE: ipam = 1; break;
   case NV50_IR_INTERP_FLAT       : ipam = 2; break;
   case NV50_IR_INTERP_SC         : ipam = 3; break;
   default:
      assert(!"invalid ipa mode");
      break;
   }

   switch (insn->getSampleMode()) {
   case NV50_IR_INTERP_DEFAULT : ipas = 0; break;
   case NV50_IR_INTERP_CENTROID: ipas = 1; break;
   case NV50_IR_INTERP_OFFSET  : ipas = 2; break;
   default:
      assert(!"invalid ipa sample mode");
      break;
   }

   emitInsn (0xe0000000);
   emitField(0x36, 2, ipam);
   emitField(0x34, 2, ipas);
   emitSAT  (0x33);
   emitField(0x2f, 3, 7);
   emitADDR (0x08, 0x1c, 10, 0, insn->src(0));
   if ((code[0] & 0x0000ff00) != 0x0000ff00)
      code[1] |= 0x00000040; /* .idx */
   emitGPR(0x00, insn->def(0));

   if (insn->op == OP_PINTERP) {
      emitGPR(0x14, insn->src(1));
      if (insn->getSampleMode() == NV50_IR_INTERP_OFFSET)
         emitGPR(0x27, insn->src(2));
      addInterp(insn->ipa, insn->getSrc(1)->reg.data.id, interpApply);
   } else {
      if (insn->getSampleMode() == NV50_IR_INTERP_OFFSET)
         emitGPR(0x27, insn->src(1));
      emitGPR(0x14);
      addInterp(insn->ipa, 0xff, interpApply);
   }

   if (insn->getSampleMode() != NV50_IR_INTERP_OFFSET)
      emitGPR(0x27);
}

void
CodeEmitterGM107::emitATOM()
{
   unsigned dType, subOp;

   if (insn->subOp == NV50_IR_SUBOP_ATOM_CAS) {
      switch (insn->dType) {
      case TYPE_U32: dType = 0; break;
      case TYPE_U64: dType = 1; break;
      default: assert(!"unexpected dType"); dType = 0; break;
      }
      subOp = 15;

      emitInsn (0xee000000);
   } else {
      switch (insn->dType) {
      case TYPE_U32: dType = 0; break;
      case TYPE_S32: dType = 1; break;
      case TYPE_U64: dType = 2; break;
      case TYPE_F32: dType = 3; break;
      case TYPE_B128: dType = 4; break;
      case TYPE_S64: dType = 5; break;
      default: assert(!"unexpected dType"); dType = 0; break;
      }
      if (insn->subOp == NV50_IR_SUBOP_ATOM_EXCH)
         subOp = 8;
      else
         subOp = insn->subOp;

      emitInsn (0xed000000);
   }

   emitField(0x34, 4, subOp);
   emitField(0x31, 3, dType);
   emitField(0x30, 1, insn->src(0).getIndirect(0)->getSize() == 8);
   emitGPR  (0x14, insn->src(1));
   emitADDR (0x08, 0x1c, 20, 0, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitATOMS()
{
   unsigned dType, subOp;

   if (insn->subOp == NV50_IR_SUBOP_ATOM_CAS) {
      switch (insn->dType) {
      case TYPE_U32: dType = 0; break;
      case TYPE_U64: dType = 1; break;
      default: assert(!"unexpected dType"); dType = 0; break;
      }
      subOp = 4;

      emitInsn (0xee000000);
      emitField(0x34, 1, dType);
   } else {
      switch (insn->dType) {
      case TYPE_U32: dType = 0; break;
      case TYPE_S32: dType = 1; break;
      case TYPE_U64: dType = 2; break;
      case TYPE_S64: dType = 3; break;
      default: assert(!"unexpected dType"); dType = 0; break;
      }

      if (insn->subOp == NV50_IR_SUBOP_ATOM_EXCH)
         subOp = 8;
      else
         subOp = insn->subOp;

      emitInsn (0xec000000);
      emitField(0x1c, 3, dType);
   }

   emitField(0x34, 4, subOp);
   emitGPR  (0x14, insn->src(1));
   emitADDR (0x08, 0x1e, 22, 2, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitRED()
{
   unsigned dType;

   switch (insn->dType) {
   case TYPE_U32: dType = 0; break;
   case TYPE_S32: dType = 1; break;
   case TYPE_U64: dType = 2; break;
   case TYPE_F32: dType = 3; break;
   case TYPE_B128: dType = 4; break;
   case TYPE_S64: dType = 5; break;
   default: assert(!"unexpected dType"); dType = 0; break;
   }

   emitInsn (0xebf80000);
   emitField(0x30, 1, insn->src(0).getIndirect(0)->getSize() == 8);
   emitField(0x17, 3, insn->subOp);
   emitField(0x14, 3, dType);
   emitADDR (0x08, 0x1c, 20, 0, insn->src(0));
   emitGPR  (0x00, insn->src(1));
}

void
CodeEmitterGM107::emitCCTL()
{
   unsigned width;
   if (insn->src(0).getFile() == FILE_MEMORY_GLOBAL) {
      emitInsn(0xef600000);
      width = 30;
   } else {
      emitInsn(0xef800000);
      width = 22;
   }
   emitField(0x34, 1, insn->src(0).getIndirect(0)->getSize() == 8);
   emitADDR (0x08, 0x16, width, 2, insn->src(0));
   emitField(0x00, 4, insn->subOp);
}

/*******************************************************************************
 * surface
 ******************************************************************************/

void
CodeEmitterGM107::emitPIXLD()
{
   emitInsn (0xefe80000);
   emitPRED (0x2d);
   emitField(0x1f, 3, insn->subOp);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

/*******************************************************************************
 * texture
 ******************************************************************************/

void
CodeEmitterGM107::emitTEXs(int pos)
{
   int src1 = insn->predSrc == 1 ? 2 : 1;
   if (insn->srcExists(src1))
      emitGPR(pos, insn->src(src1));
   else
      emitGPR(pos);
}

void
CodeEmitterGM107::emitTEX()
{
   const TexInstruction *insn = this->insn->asTex();
   int lodm = 0;

   if (!insn->tex.levelZero) {
      switch (insn->op) {
      case OP_TEX: lodm = 0; break;
      case OP_TXB: lodm = 2; break;
      case OP_TXL: lodm = 3; break;
      default:
         assert(!"invalid tex op");
         break;
      }
   } else {
      lodm = 1;
   }

   if (insn->tex.rIndirectSrc >= 0) {
      emitInsn (0xdeb80000);
      emitField(0x25, 2, lodm);
      emitField(0x24, 1, insn->tex.useOffsets == 1);
   } else {
      emitInsn (0xc0380000);
      emitField(0x37, 2, lodm);
      emitField(0x36, 1, insn->tex.useOffsets == 1);
      emitField(0x24, 13, insn->tex.r);
   }

   emitField(0x32, 1, insn->tex.target.isShadow());
   emitField(0x31, 1, insn->tex.liveOnly);
   emitField(0x23, 1, insn->tex.derivAll);
   emitField(0x1f, 4, insn->tex.mask);
   emitField(0x1d, 2, insn->tex.target.isCube() ? 3 :
                      insn->tex.target.getDim() - 1);
   emitField(0x1c, 1, insn->tex.target.isArray());
   emitTEXs (0x14);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitTLD()
{
   const TexInstruction *insn = this->insn->asTex();

   if (insn->tex.rIndirectSrc >= 0) {
      emitInsn (0xdd380000);
   } else {
      emitInsn (0xdc380000);
      emitField(0x24, 13, insn->tex.r);
   }

   emitField(0x37, 1, insn->tex.levelZero == 0);
   emitField(0x32, 1, insn->tex.target.isMS());
   emitField(0x31, 1, insn->tex.liveOnly);
   emitField(0x23, 1, insn->tex.useOffsets == 1);
   emitField(0x1f, 4, insn->tex.mask);
   emitField(0x1d, 2, insn->tex.target.isCube() ? 3 :
                      insn->tex.target.getDim() - 1);
   emitField(0x1c, 1, insn->tex.target.isArray());
   emitTEXs (0x14);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitTLD4()
{
   const TexInstruction *insn = this->insn->asTex();

   if (insn->tex.rIndirectSrc >= 0) {
      emitInsn (0xdef80000);
      emitField(0x26, 2, insn->tex.gatherComp);
      emitField(0x25, 2, insn->tex.useOffsets == 4);
      emitField(0x24, 2, insn->tex.useOffsets == 1);
   } else {
      emitInsn (0xc8380000);
      emitField(0x38, 2, insn->tex.gatherComp);
      emitField(0x37, 2, insn->tex.useOffsets == 4);
      emitField(0x36, 2, insn->tex.useOffsets == 1);
      emitField(0x24, 13, insn->tex.r);
   }

   emitField(0x32, 1, insn->tex.target.isShadow());
   emitField(0x31, 1, insn->tex.liveOnly);
   emitField(0x23, 1, insn->tex.derivAll);
   emitField(0x1f, 4, insn->tex.mask);
   emitField(0x1d, 2, insn->tex.target.isCube() ? 3 :
                      insn->tex.target.getDim() - 1);
   emitField(0x1c, 1, insn->tex.target.isArray());
   emitTEXs (0x14);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitTXD()
{
   const TexInstruction *insn = this->insn->asTex();

   if (insn->tex.rIndirectSrc >= 0) {
      emitInsn (0xde780000);
   } else {
      emitInsn (0xde380000);
      emitField(0x24, 13, insn->tex.r);
   }

   emitField(0x31, 1, insn->tex.liveOnly);
   emitField(0x23, 1, insn->tex.useOffsets == 1);
   emitField(0x1f, 4, insn->tex.mask);
   emitField(0x1d, 2, insn->tex.target.isCube() ? 3 :
                      insn->tex.target.getDim() - 1);
   emitField(0x1c, 1, insn->tex.target.isArray());
   emitTEXs (0x14);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitTMML()
{
   const TexInstruction *insn = this->insn->asTex();

   if (insn->tex.rIndirectSrc >= 0) {
      emitInsn (0xdf600000);
   } else {
      emitInsn (0xdf580000);
      emitField(0x24, 13, insn->tex.r);
   }

   emitField(0x31, 1, insn->tex.liveOnly);
   emitField(0x23, 1, insn->tex.derivAll);
   emitField(0x1f, 4, insn->tex.mask);
   emitField(0x1d, 2, insn->tex.target.isCube() ? 3 :
                      insn->tex.target.getDim() - 1);
   emitField(0x1c, 1, insn->tex.target.isArray());
   emitTEXs (0x14);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitTXQ()
{
   const TexInstruction *insn = this->insn->asTex();
   int type = 0;

   switch (insn->tex.query) {
   case TXQ_DIMS           : type = 0x01; break;
   case TXQ_TYPE           : type = 0x02; break;
   case TXQ_SAMPLE_POSITION: type = 0x05; break;
   case TXQ_FILTER         : type = 0x10; break;
   case TXQ_LOD            : type = 0x12; break;
   case TXQ_WRAP           : type = 0x14; break;
   case TXQ_BORDER_COLOUR  : type = 0x16; break;
   default:
      assert(!"invalid txq query");
      break;
   }

   if (insn->tex.rIndirectSrc >= 0) {
      emitInsn (0xdf500000);
   } else {
      emitInsn (0xdf480000);
      emitField(0x24, 13, insn->tex.r);
   }

   emitField(0x31, 1, insn->tex.liveOnly);
   emitField(0x1f, 4, insn->tex.mask);
   emitField(0x16, 6, type);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitDEPBAR()
{
   emitInsn (0xf0f00000);
   emitField(0x1d, 1, 1); /* le */
   emitField(0x1a, 3, 5);
   emitField(0x14, 6, insn->subOp);
   emitField(0x00, 6, insn->subOp);
}

/*******************************************************************************
 * misc
 ******************************************************************************/

void
CodeEmitterGM107::emitNOP()
{
   emitInsn(0x50b00000);
}

void
CodeEmitterGM107::emitKIL()
{
   emitInsn (0xe3300000);
   emitCond5(0x00, CC_TR);
}

void
CodeEmitterGM107::emitOUT()
{
   const int cut  = insn->op == OP_RESTART || insn->subOp;
   const int emit = insn->op == OP_EMIT;

   switch (insn->src(1).getFile()) {
   case FILE_GPR:
      emitInsn(0xfbe00000);
      emitGPR (0x14, insn->src(1));
      break;
   case FILE_IMMEDIATE:
      emitInsn(0xf6e00000);
      emitIMMD(0x14, 19, insn->src(1));
      break;
   case FILE_MEMORY_CONST:
      emitInsn(0xebe00000);
      emitCBUF(0x22, -1, 0x14, 16, 2, insn->src(1));
      break;
   default:
      assert(!"bad src1 file");
      break;
   }

   emitField(0x27, 2, (cut << 1) | emit);
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));
}

void
CodeEmitterGM107::emitBAR()
{
   uint8_t subop;

   emitInsn (0xf0a80000);

   switch (insn->subOp) {
   case NV50_IR_SUBOP_BAR_RED_POPC: subop = 0x02; break;
   case NV50_IR_SUBOP_BAR_RED_AND:  subop = 0x0a; break;
   case NV50_IR_SUBOP_BAR_RED_OR:   subop = 0x12; break;
   case NV50_IR_SUBOP_BAR_ARRIVE:   subop = 0x81; break;
   default:
      subop = 0x80;
      assert(insn->subOp == NV50_IR_SUBOP_BAR_SYNC);
      break;
   }

   emitField(0x20, 8, subop);

   // barrier id
   if (insn->src(0).getFile() == FILE_GPR) {
      emitGPR(0x08, insn->src(0));
   } else {
      ImmediateValue *imm = insn->getSrc(0)->asImm();
      assert(imm);
      emitField(0x08, 8, imm->reg.data.u32);
      emitField(0x2b, 1, 1);
   }

   // thread count
   if (insn->src(1).getFile() == FILE_GPR) {
      emitGPR(0x14, insn->src(1));
   } else {
      ImmediateValue *imm = insn->getSrc(0)->asImm();
      assert(imm);
      emitField(0x14, 12, imm->reg.data.u32);
      emitField(0x2c, 1, 1);
   }

   if (insn->srcExists(2) && (insn->predSrc != 2)) {
      emitPRED (0x27, insn->src(2));
      emitField(0x2a, 1, insn->src(2).mod == Modifier(NV50_IR_MOD_NOT));
   } else {
      emitField(0x27, 3, 7);
   }
}

void
CodeEmitterGM107::emitMEMBAR()
{
   emitInsn (0xef980000);
   emitField(0x08, 2, insn->subOp >> 2);
}

void
CodeEmitterGM107::emitVOTE()
{
   const ImmediateValue *imm;
   uint32_t u32;

   int r = -1, p = -1;
   for (int i = 0; insn->defExists(i); i++) {
      if (insn->def(i).getFile() == FILE_GPR)
         r = i;
      else if (insn->def(i).getFile() == FILE_PREDICATE)
         p = i;
   }

   emitInsn (0x50d80000);
   emitField(0x30, 2, insn->subOp);
   if (r >= 0)
      emitGPR  (0x00, insn->def(r));
   else
      emitGPR  (0x00);
   if (p >= 0)
      emitPRED (0x2d, insn->def(p));
   else
      emitPRED (0x2d);

   switch (insn->src(0).getFile()) {
   case FILE_PREDICATE:
      emitField(0x2a, 1, insn->src(0).mod == Modifier(NV50_IR_MOD_NOT));
      emitPRED (0x27, insn->src(0));
      break;
   case FILE_IMMEDIATE:
      imm = insn->getSrc(0)->asImm();
      assert(imm);
      u32 = imm->reg.data.u32;
      assert(u32 == 0 || u32 == 1);
      emitPRED(0x27);
      emitField(0x2a, 1, u32 == 0);
      break;
   default:
      assert(!"Unhandled src");
      break;
   }
}

void
CodeEmitterGM107::emitSUTarget()
{
   const TexInstruction *insn = this->insn->asTex();
   int target = 0;

   assert(insn->op >= OP_SULDB && insn->op <= OP_SUREDP);

   if (insn->tex.target == TEX_TARGET_BUFFER) {
      target = 2;
   } else if (insn->tex.target == TEX_TARGET_1D_ARRAY) {
      target = 4;
   } else if (insn->tex.target == TEX_TARGET_2D ||
              insn->tex.target == TEX_TARGET_RECT) {
      target = 6;
   } else if (insn->tex.target == TEX_TARGET_2D_ARRAY ||
              insn->tex.target == TEX_TARGET_CUBE ||
              insn->tex.target == TEX_TARGET_CUBE_ARRAY) {
      target = 8;
   } else if (insn->tex.target == TEX_TARGET_3D) {
      target = 10;
   } else {
      assert(insn->tex.target == TEX_TARGET_1D);
   }
   emitField(0x20, 4, target);
}

void
CodeEmitterGM107::emitSUHandle(const int s)
{
   const TexInstruction *insn = this->insn->asTex();

   assert(insn->op >= OP_SULDB && insn->op <= OP_SUREDP);

   if (insn->src(s).getFile() == FILE_GPR) {
      emitGPR(0x27, insn->src(s));
   } else {
      ImmediateValue *imm = insn->getSrc(s)->asImm();
      assert(imm);
      emitField(0x33, 1, 1);
      emitField(0x24, 13, imm->reg.data.u32);
   }
}

void
CodeEmitterGM107::emitSUSTx()
{
   const TexInstruction *insn = this->insn->asTex();

   emitInsn(0xeb200000);
   if (insn->op == OP_SUSTB)
      emitField(0x34, 1, 1);
   emitSUTarget();

   emitLDSTc(0x18);
   emitField(0x14, 4, 0xf); // rgba
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->src(1));

   emitSUHandle(2);
}

void
CodeEmitterGM107::emitSULDx()
{
   const TexInstruction *insn = this->insn->asTex();
   int type = 0;

   emitInsn(0xeb000000);
   if (insn->op == OP_SULDB)
      emitField(0x34, 1, 1);
   emitSUTarget();

   switch (insn->dType) {
   case TYPE_S8:   type = 1; break;
   case TYPE_U16:  type = 2; break;
   case TYPE_S16:  type = 3; break;
   case TYPE_U32:  type = 4; break;
   case TYPE_U64:  type = 5; break;
   case TYPE_B128: type = 6; break;
   default:
      assert(insn->dType == TYPE_U8);
      break;
   }
   emitLDSTc(0x18);
   emitField(0x14, 3, type);
   emitGPR  (0x00, insn->def(0));
   emitGPR  (0x08, insn->src(0));

   emitSUHandle(1);
}

void
CodeEmitterGM107::emitSUREDx()
{
   const TexInstruction *insn = this->insn->asTex();
   uint8_t type = 0, subOp;

   if (insn->subOp == NV50_IR_SUBOP_ATOM_CAS)
      emitInsn(0xeac00000);
   else
      emitInsn(0xea600000);

   if (insn->op == OP_SUREDB)
      emitField(0x34, 1, 1);
   emitSUTarget();

   // destination type
   switch (insn->dType) {
   case TYPE_S32: type = 1; break;
   case TYPE_U64: type = 2; break;
   case TYPE_F32: type = 3; break;
   case TYPE_S64: type = 5; break;
   default:
      assert(insn->dType == TYPE_U32);
      break;
   }

   // atomic operation
   if (insn->subOp == NV50_IR_SUBOP_ATOM_CAS) {
      subOp = 0;
   } else if (insn->subOp == NV50_IR_SUBOP_ATOM_EXCH) {
      subOp = 8;
   } else {
      subOp = insn->subOp;
   }

   emitField(0x24, 3, type);
   emitField(0x1d, 4, subOp);
   emitGPR  (0x14, insn->src(1));
   emitGPR  (0x08, insn->src(0));
   emitGPR  (0x00, insn->def(0));

   emitSUHandle(2);
}

/*******************************************************************************
 * assembler front-end
 ******************************************************************************/

bool
CodeEmitterGM107::emitInstruction(Instruction *i)
{
   const unsigned int size = (writeIssueDelays && !(codeSize & 0x1f)) ? 16 : 8;
   bool ret = true;

   insn = i;

   if (insn->encSize != 8) {
      ERROR("skipping undecodable instruction: "); insn->print();
      return false;
   } else
   if (codeSize + size > codeSizeLimit) {
      ERROR("code emitter output buffer too small\n");
      return false;
   }

   if (writeIssueDelays) {
      int n = ((codeSize & 0x1f) / 8) - 1;
      if (n < 0) {
         data = code;
         data[0] = 0x00000000;
         data[1] = 0x00000000;
         code += 2;
         codeSize += 8;
         n++;
      }

      emitField(data, n * 21, 21, insn->sched);
   }

   switch (insn->op) {
   case OP_EXIT:
      emitEXIT();
      break;
   case OP_BRA:
      emitBRA();
      break;
   case OP_CALL:
      emitCAL();
      break;
   case OP_PRECONT:
      emitPCNT();
      break;
   case OP_CONT:
      emitCONT();
      break;
   case OP_PREBREAK:
      emitPBK();
      break;
   case OP_BREAK:
      emitBRK();
      break;
   case OP_PRERET:
      emitPRET();
      break;
   case OP_RET:
      emitRET();
      break;
   case OP_JOINAT:
      emitSSY();
      break;
   case OP_JOIN:
      emitSYNC();
      break;
   case OP_QUADON:
      emitSAM();
      break;
   case OP_QUADPOP:
      emitRAM();
      break;
   case OP_MOV:
      emitMOV();
      break;
   case OP_RDSV:
      emitS2R();
      break;
   case OP_ABS:
   case OP_NEG:
   case OP_SAT:
   case OP_FLOOR:
   case OP_CEIL:
   case OP_TRUNC:
   case OP_CVT:
      if (insn->op == OP_CVT && (insn->def(0).getFile() == FILE_PREDICATE ||
                                 insn->src(0).getFile() == FILE_PREDICATE)) {
         emitMOV();
      } else if (isFloatType(insn->dType)) {
         if (isFloatType(insn->sType))
            emitF2F();
         else
            emitI2F();
      } else {
         if (isFloatType(insn->sType))
            emitF2I();
         else
            emitI2I();
      }
      break;
   case OP_SHFL:
      emitSHFL();
      break;
   case OP_ADD:
   case OP_SUB:
      if (isFloatType(insn->dType)) {
         if (insn->dType == TYPE_F64)
            emitDADD();
         else
            emitFADD();
      } else {
         emitIADD();
      }
      break;
   case OP_MUL:
      if (isFloatType(insn->dType)) {
         if (insn->dType == TYPE_F64)
            emitDMUL();
         else
            emitFMUL();
      } else {
         emitIMUL();
      }
      break;
   case OP_MAD:
   case OP_FMA:
      if (isFloatType(insn->dType)) {
         if (insn->dType == TYPE_F64)
            emitDFMA();
         else
            emitFFMA();
      } else {
         emitIMAD();
      }
      break;
   case OP_SHLADD:
      emitISCADD();
      break;
   case OP_MIN:
   case OP_MAX:
      if (isFloatType(insn->dType)) {
         if (insn->dType == TYPE_F64)
            emitDMNMX();
         else
            emitFMNMX();
      } else {
         emitIMNMX();
      }
      break;
   case OP_SHL:
      if (typeSizeof(insn->sType) == 8)
         emitSHF();
      else
         emitSHL();
      break;
   case OP_SHR:
      if (typeSizeof(insn->sType) == 8)
         emitSHF();
      else
         emitSHR();
      break;
   case OP_POPCNT:
      emitPOPC();
      break;
   case OP_INSBF:
      emitBFI();
      break;
   case OP_EXTBF:
      emitBFE();
      break;
   case OP_BFIND:
      emitFLO();
      break;
   case OP_SLCT:
      if (isFloatType(insn->dType))
         emitFCMP();
      else
         emitICMP();
      break;
   case OP_SET:
   case OP_SET_AND:
   case OP_SET_OR:
   case OP_SET_XOR:
      if (insn->def(0).getFile() != FILE_PREDICATE) {
         if (isFloatType(insn->sType))
            if (insn->sType == TYPE_F64)
               emitDSET();
            else
               emitFSET();
         else
            emitISET();
      } else {
         if (isFloatType(insn->sType))
            if (insn->sType == TYPE_F64)
               emitDSETP();
            else
               emitFSETP();
         else
            emitISETP();
      }
      break;
   case OP_SELP:
      emitSEL();
      break;
   case OP_PRESIN:
   case OP_PREEX2:
      emitRRO();
      break;
   case OP_COS:
   case OP_SIN:
   case OP_EX2:
   case OP_LG2:
   case OP_RCP:
   case OP_RSQ:
      emitMUFU();
      break;
   case OP_AND:
   case OP_OR:
   case OP_XOR:
      emitLOP();
      break;
   case OP_NOT:
      emitNOT();
      break;
   case OP_LOAD:
      switch (insn->src(0).getFile()) {
      case FILE_MEMORY_CONST : emitLDC(); break;
      case FILE_MEMORY_LOCAL : emitLDL(); break;
      case FILE_MEMORY_SHARED: emitLDS(); break;
      case FILE_MEMORY_GLOBAL: emitLD(); break;
      default:
         assert(!"invalid load");
         emitNOP();
         break;
      }
      break;
   case OP_STORE:
      switch (insn->src(0).getFile()) {
      case FILE_MEMORY_LOCAL : emitSTL(); break;
      case FILE_MEMORY_SHARED: emitSTS(); break;
      case FILE_MEMORY_GLOBAL: emitST(); break;
      default:
         assert(!"invalid store");
         emitNOP();
         break;
      }
      break;
   case OP_ATOM:
      if (insn->src(0).getFile() == FILE_MEMORY_SHARED)
         emitATOMS();
      else
         if (!insn->defExists(0) && insn->subOp < NV50_IR_SUBOP_ATOM_CAS)
            emitRED();
         else
            emitATOM();
      break;
   case OP_CCTL:
      emitCCTL();
      break;
   case OP_VFETCH:
      emitALD();
      break;
   case OP_EXPORT:
      emitAST();
      break;
   case OP_PFETCH:
      emitISBERD();
      break;
   case OP_AFETCH:
      emitAL2P();
      break;
   case OP_LINTERP:
   case OP_PINTERP:
      emitIPA();
      break;
   case OP_PIXLD:
      emitPIXLD();
      break;
   case OP_TEX:
   case OP_TXB:
   case OP_TXL:
      emitTEX();
      break;
   case OP_TXF:
      emitTLD();
      break;
   case OP_TXG:
      emitTLD4();
      break;
   case OP_TXD:
      emitTXD();
      break;
   case OP_TXQ:
      emitTXQ();
      break;
   case OP_TXLQ:
      emitTMML();
      break;
   case OP_TEXBAR:
      emitDEPBAR();
      break;
   case OP_QUADOP:
      emitFSWZADD();
      break;
   case OP_NOP:
      emitNOP();
      break;
   case OP_DISCARD:
      emitKIL();
      break;
   case OP_EMIT:
   case OP_RESTART:
      emitOUT();
      break;
   case OP_BAR:
      emitBAR();
      break;
   case OP_MEMBAR:
      emitMEMBAR();
      break;
   case OP_VOTE:
      emitVOTE();
      break;
   case OP_SUSTB:
   case OP_SUSTP:
      emitSUSTx();
      break;
   case OP_SULDB:
   case OP_SULDP:
      emitSULDx();
      break;
   case OP_SUREDB:
   case OP_SUREDP:
      emitSUREDx();
      break;
   default:
      assert(!"invalid opcode");
      emitNOP();
      ret = false;
      break;
   }

   if (insn->join) {
      /*XXX*/
   }

   code += 2;
   codeSize += 8;
   return ret;
}

uint32_t
CodeEmitterGM107::getMinEncodingSize(const Instruction *i) const
{
   return 8;
}

/*******************************************************************************
 * sched data calculator
 ******************************************************************************/

class SchedDataCalculatorGM107 : public Pass
{
public:
   SchedDataCalculatorGM107(const TargetGM107 *targ) : targ(targ) {}

private:
   struct RegScores
   {
      struct ScoreData {
         int r[256];
         int p[8];
         int c;
      } rd, wr;
      int base;

      void rebase(const int base)
      {
         const int delta = this->base - base;
         if (!delta)
            return;
         this->base = 0;

         for (int i = 0; i < 256; ++i) {
            rd.r[i] += delta;
            wr.r[i] += delta;
         }
         for (int i = 0; i < 8; ++i) {
            rd.p[i] += delta;
            wr.p[i] += delta;
         }
         rd.c += delta;
         wr.c += delta;
      }
      void wipe()
      {
         memset(&rd, 0, sizeof(rd));
         memset(&wr, 0, sizeof(wr));
      }
      int getLatest(const ScoreData& d) const
      {
         int max = 0;
         for (int i = 0; i < 256; ++i)
            if (d.r[i] > max)
               max = d.r[i];
         for (int i = 0; i < 8; ++i)
            if (d.p[i] > max)
               max = d.p[i];
         if (d.c > max)
            max = d.c;
         return max;
      }
      inline int getLatestRd() const
      {
         return getLatest(rd);
      }
      inline int getLatestWr() const
      {
         return getLatest(wr);
      }
      inline int getLatest() const
      {
         return MAX2(getLatestRd(), getLatestWr());
      }
      void setMax(const RegScores *that)
      {
         for (int i = 0; i < 256; ++i) {
            rd.r[i] = MAX2(rd.r[i], that->rd.r[i]);
            wr.r[i] = MAX2(wr.r[i], that->wr.r[i]);
         }
         for (int i = 0; i < 8; ++i) {
            rd.p[i] = MAX2(rd.p[i], that->rd.p[i]);
            wr.p[i] = MAX2(wr.p[i], that->wr.p[i]);
         }
         rd.c = MAX2(rd.c, that->rd.c);
         wr.c = MAX2(wr.c, that->wr.c);
      }
      void print(int cycle)
      {
         for (int i = 0; i < 256; ++i) {
            if (rd.r[i] > cycle)
               INFO("rd $r%i @ %i\n", i, rd.r[i]);
            if (wr.r[i] > cycle)
               INFO("wr $r%i @ %i\n", i, wr.r[i]);
         }
         for (int i = 0; i < 8; ++i) {
            if (rd.p[i] > cycle)
               INFO("rd $p%i @ %i\n", i, rd.p[i]);
            if (wr.p[i] > cycle)
               INFO("wr $p%i @ %i\n", i, wr.p[i]);
         }
         if (rd.c > cycle)
            INFO("rd $c @ %i\n", rd.c);
         if (wr.c > cycle)
            INFO("wr $c @ %i\n", wr.c);
      }
   };

   RegScores *score; // for current BB
   std::vector<RegScores> scoreBoards;

   const TargetGM107 *targ;
   bool visit(Function *);
   bool visit(BasicBlock *);

   void commitInsn(const Instruction *, int);
   int calcDelay(const Instruction *, int) const;
   void setDelay(Instruction *, int, const Instruction *);
   void recordWr(const Value *, int, int);
   void checkRd(const Value *, int, int&) const;

   inline void emitYield(Instruction *);
   inline void emitStall(Instruction *, uint8_t);
   inline void emitReuse(Instruction *, uint8_t);
   inline void emitWrDepBar(Instruction *, uint8_t);
   inline void emitRdDepBar(Instruction *, uint8_t);
   inline void emitWtDepBar(Instruction *, uint8_t);

   inline int getStall(const Instruction *) const;
   inline int getWrDepBar(const Instruction *) const;
   inline int getRdDepBar(const Instruction *) const;
   inline int getWtDepBar(const Instruction *) const;

   void setReuseFlag(Instruction *);

   inline void printSchedInfo(int, const Instruction *) const;

   struct LiveBarUse {
      LiveBarUse(Instruction *insn, Instruction *usei)
         : insn(insn), usei(usei) { }
      Instruction *insn;
      Instruction *usei;
   };

   struct LiveBarDef {
      LiveBarDef(Instruction *insn, Instruction *defi)
         : insn(insn), defi(defi) { }
      Instruction *insn;
      Instruction *defi;
   };

   bool insertBarriers(BasicBlock *);

   Instruction *findFirstUse(const Instruction *) const;
   Instruction *findFirstDef(const Instruction *) const;

   bool needRdDepBar(const Instruction *) const;
   bool needWrDepBar(const Instruction *) const;
};

inline void
SchedDataCalculatorGM107::emitStall(Instruction *insn, uint8_t cnt)
{
   assert(cnt < 16);
   insn->sched |= cnt;
}

inline void
SchedDataCalculatorGM107::emitYield(Instruction *insn)
{
   insn->sched |= 1 << 4;
}

inline void
SchedDataCalculatorGM107::emitWrDepBar(Instruction *insn, uint8_t id)
{
   assert(id < 6);
   if ((insn->sched & 0xe0) == 0xe0)
      insn->sched ^= 0xe0;
   insn->sched |= id << 5;
}

inline void
SchedDataCalculatorGM107::emitRdDepBar(Instruction *insn, uint8_t id)
{
   assert(id < 6);
   if ((insn->sched & 0x700) == 0x700)
      insn->sched ^= 0x700;
   insn->sched |= id << 8;
}

inline void
SchedDataCalculatorGM107::emitWtDepBar(Instruction *insn, uint8_t id)
{
   assert(id < 6);
   insn->sched |= 1 << (11 + id);
}

inline void
SchedDataCalculatorGM107::emitReuse(Instruction *insn, uint8_t id)
{
   assert(id < 4);
   insn->sched |= 1 << (17 + id);
}

inline void
SchedDataCalculatorGM107::printSchedInfo(int cycle,
                                         const Instruction *insn) const
{
   uint8_t st, yl, wr, rd, wt, ru;

   st = (insn->sched & 0x00000f) >> 0;
   yl = (insn->sched & 0x000010) >> 4;
   wr = (insn->sched & 0x0000e0) >> 5;
   rd = (insn->sched & 0x000700) >> 8;
   wt = (insn->sched & 0x01f800) >> 11;
   ru = (insn->sched & 0x1e0000) >> 17;

   INFO("cycle %i, (st 0x%x, yl 0x%x, wr 0x%x, rd 0x%x, wt 0x%x, ru 0x%x)\n",
        cycle, st, yl, wr, rd, wt, ru);
}

inline int
SchedDataCalculatorGM107::getStall(const Instruction *insn) const
{
   return insn->sched & 0xf;
}

inline int
SchedDataCalculatorGM107::getWrDepBar(const Instruction *insn) const
{
   return (insn->sched & 0x0000e0) >> 5;
}

inline int
SchedDataCalculatorGM107::getRdDepBar(const Instruction *insn) const
{
   return (insn->sched & 0x000700) >> 8;
}

inline int
SchedDataCalculatorGM107::getWtDepBar(const Instruction *insn) const
{
   return (insn->sched & 0x01f800) >> 11;
}

// Emit the reuse flag which allows to make use of the new memory hierarchy
// introduced since Maxwell, the operand reuse cache.
//
// It allows to reduce bank conflicts by caching operands. Each time you issue
// an instruction, that flag can tell the hw which operands are going to be
// re-used by the next instruction. Note that the next instruction has to use
// the same GPR id in the same operand slot.
void
SchedDataCalculatorGM107::setReuseFlag(Instruction *insn)
{
   Instruction *next = insn->next;
   BitSet defs(255, 1);

   if (!targ->isReuseSupported(insn))
      return;

   for (int d = 0; insn->defExists(d); ++d) {
      const Value *def = insn->def(d).rep();
      if (insn->def(d).getFile() != FILE_GPR)
         continue;
      if (typeSizeof(insn->dType) != 4 || def->reg.data.id == 255)
         continue;
      defs.set(def->reg.data.id);
   }

   for (int s = 0; insn->srcExists(s); s++) {
      const Value *src = insn->src(s).rep();
      if (insn->src(s).getFile() != FILE_GPR)
         continue;
      if (typeSizeof(insn->sType) != 4 || src->reg.data.id == 255)
         continue;
      if (defs.test(src->reg.data.id))
         continue;
      if (!next->srcExists(s) || next->src(s).getFile() != FILE_GPR)
         continue;
      if (src->reg.data.id != next->getSrc(s)->reg.data.id)
         continue;
      assert(s < 4);
      emitReuse(insn, s);
   }
}

void
SchedDataCalculatorGM107::recordWr(const Value *v, int cycle, int ready)
{
   int a = v->reg.data.id, b;

   switch (v->reg.file) {
   case FILE_GPR:
      b = a + v->reg.size / 4;
      for (int r = a; r < b; ++r)
         score->rd.r[r] = ready;
      break;
   case FILE_PREDICATE:
      // To immediately use a predicate set by any instructions, the minimum
      // number of stall counts is 13.
      score->rd.p[a] = cycle + 13;
      break;
   case FILE_FLAGS:
      score->rd.c = ready;
      break;
   default:
      break;
   }
}

void
SchedDataCalculatorGM107::checkRd(const Value *v, int cycle, int &delay) const
{
   int a = v->reg.data.id, b;
   int ready = cycle;

   switch (v->reg.file) {
   case FILE_GPR:
      b = a + v->reg.size / 4;
      for (int r = a; r < b; ++r)
         ready = MAX2(ready, score->rd.r[r]);
      break;
   case FILE_PREDICATE:
      ready = MAX2(ready, score->rd.p[a]);
      break;
   case FILE_FLAGS:
      ready = MAX2(ready, score->rd.c);
      break;
   default:
      break;
   }
   if (cycle < ready)
      delay = MAX2(delay, ready - cycle);
}

void
SchedDataCalculatorGM107::commitInsn(const Instruction *insn, int cycle)
{
   const int ready = cycle + targ->getLatency(insn);

   for (int d = 0; insn->defExists(d); ++d)
      recordWr(insn->getDef(d), cycle, ready);

#ifdef GM107_DEBUG_SCHED_DATA
   score->print(cycle);
#endif
}

#define GM107_MIN_ISSUE_DELAY 0x1
#define GM107_MAX_ISSUE_DELAY 0xf

int
SchedDataCalculatorGM107::calcDelay(const Instruction *insn, int cycle) const
{
   int delay = 0, ready = cycle;

   for (int s = 0; insn->srcExists(s); ++s)
      checkRd(insn->getSrc(s), cycle, delay);

   // TODO: make use of getReadLatency()!

   return MAX2(delay, ready - cycle);
}

void
SchedDataCalculatorGM107::setDelay(Instruction *insn, int delay,
                                   const Instruction *next)
{
   const OpClass cl = targ->getOpClass(insn->op);
   int wr, rd;

   if (insn->op == OP_EXIT ||
       insn->op == OP_BAR ||
       insn->op == OP_MEMBAR) {
      delay = GM107_MAX_ISSUE_DELAY;
   } else
   if (insn->op == OP_QUADON ||
       insn->op == OP_QUADPOP) {
      delay = 0xd;
   } else
   if (cl == OPCLASS_FLOW || insn->join) {
      delay = 0xd;
   }

   if (!next || !targ->canDualIssue(insn, next)) {
      delay = CLAMP(delay, GM107_MIN_ISSUE_DELAY, GM107_MAX_ISSUE_DELAY);
   } else {
      delay = 0x0; // dual-issue
   }

   wr = getWrDepBar(insn);
   rd = getRdDepBar(insn);

   if (delay == GM107_MIN_ISSUE_DELAY && (wr & rd) != 7) {
      // Barriers take one additional clock cycle to become active on top of
      // the clock consumed by the instruction producing it.
      if (!next || insn->bb != next->bb) {
         delay = 0x2;
      } else {
         int wt = getWtDepBar(next);
         if ((wt & (1 << wr)) | (wt & (1 << rd)))
            delay = 0x2;
      }
   }

   emitStall(insn, delay);
}


// Return true when the given instruction needs to emit a read dependency
// barrier (for WaR hazards) because it doesn't operate at a fixed latency, and
// setting the maximum number of stall counts is not enough.
bool
SchedDataCalculatorGM107::needRdDepBar(const Instruction *insn) const
{
   BitSet srcs(255, 1), defs(255, 1);
   int a, b;

   if (!targ->isBarrierRequired(insn))
      return false;

   // Do not emit a read dependency barrier when the instruction doesn't use
   // any GPR (like st s[0x4] 0x0) as input because it's unnecessary.
   for (int s = 0; insn->srcExists(s); ++s) {
      const Value *src = insn->src(s).rep();
      if (insn->src(s).getFile() != FILE_GPR)
         continue;
      if (src->reg.data.id == 255)
         continue;

      a = src->reg.data.id;
      b = a + src->reg.size / 4;
      for (int r = a; r < b; ++r)
         srcs.set(r);
   }

   if (!srcs.popCount())
      return false;

   // Do not emit a read dependency barrier when the output GPRs are equal to
   // the input GPRs (like rcp $r0 $r0) because a write dependency barrier will
   // be produced and WaR hazards are prevented.
   for (int d = 0; insn->defExists(d); ++d) {
      const Value *def = insn->def(d).rep();
      if (insn->def(d).getFile() != FILE_GPR)
         continue;
      if (def->reg.data.id == 255)
         continue;

      a = def->reg.data.id;
      b = a + def->reg.size / 4;
      for (int r = a; r < b; ++r)
         defs.set(r);
   }

   srcs.andNot(defs);
   if (!srcs.popCount())
      return false;

   return true;
}

// Return true when the given instruction needs to emit a write dependency
// barrier (for RaW hazards) because it doesn't operate at a fixed latency, and
// setting the maximum number of stall counts is not enough. This is only legal
// if the instruction output something.
bool
SchedDataCalculatorGM107::needWrDepBar(const Instruction *insn) const
{
   if (!targ->isBarrierRequired(insn))
      return false;

   for (int d = 0; insn->defExists(d); ++d) {
      if (insn->def(d).getFile() == FILE_GPR ||
          insn->def(d).getFile() == FILE_PREDICATE)
         return true;
   }
   return false;
}

// Find the next instruction inside the same basic block which uses the output
// of the given instruction in order to avoid RaW hazards.
Instruction *
SchedDataCalculatorGM107::findFirstUse(const Instruction *bari) const
{
   Instruction *insn, *next;
   int minGPR, maxGPR;

   if (!bari->defExists(0))
      return NULL;

   minGPR = bari->def(0).rep()->reg.data.id;
   maxGPR = minGPR + bari->def(0).rep()->reg.size / 4 - 1;

   for (insn = bari->next; insn != NULL; insn = next) {
      next = insn->next;

      for (int s = 0; insn->srcExists(s); ++s) {
         const Value *src = insn->src(s).rep();
         if (bari->def(0).getFile() == FILE_GPR) {
            if (insn->src(s).getFile() != FILE_GPR ||
                src->reg.data.id + src->reg.size / 4 - 1 < minGPR ||
                src->reg.data.id > maxGPR)
               continue;
            return insn;
         } else
         if (bari->def(0).getFile() == FILE_PREDICATE) {
            if (insn->src(s).getFile() != FILE_PREDICATE ||
                src->reg.data.id != minGPR)
               continue;
            return insn;
         }
      }
   }
   return NULL;
}

// Find the next instruction inside the same basic block which overwrites, at
// least, one source of the given instruction in order to avoid WaR hazards.
Instruction *
SchedDataCalculatorGM107::findFirstDef(const Instruction *bari) const
{
   Instruction *insn, *next;
   int minGPR, maxGPR;

   for (insn = bari->next; insn != NULL; insn = next) {
      next = insn->next;

      for (int d = 0; insn->defExists(d); ++d) {
         const Value *def = insn->def(d).rep();
         if (insn->def(d).getFile() != FILE_GPR)
            continue;

         minGPR = def->reg.data.id;
         maxGPR = minGPR + def->reg.size / 4 - 1;

         for (int s = 0; bari->srcExists(s); ++s) {
            const Value *src = bari->src(s).rep();
            if (bari->src(s).getFile() != FILE_GPR ||
                src->reg.data.id + src->reg.size / 4 - 1 < minGPR ||
                src->reg.data.id > maxGPR)
               continue;
            return insn;
         }
      }
   }
   return NULL;
}

// Dependency barriers:
// This pass is a bit ugly and could probably be improved by performing a
// better allocation.
//
// The main idea is to avoid WaR and RaW hazards by emitting read/write
// dependency barriers using the control codes.
bool
SchedDataCalculatorGM107::insertBarriers(BasicBlock *bb)
{
   std::list<LiveBarUse> live_uses;
   std::list<LiveBarDef> live_defs;
   Instruction *insn, *next;
   BitSet bars(6, 1);
   int bar_id;

   for (insn = bb->getEntry(); insn != NULL; insn = next) {
      Instruction *usei = NULL, *defi = NULL;
      bool need_wr_bar, need_rd_bar;

      next = insn->next;

      // Expire old barrier uses.
      for (std::list<LiveBarUse>::iterator it = live_uses.begin();
           it != live_uses.end();) {
         if (insn->serial >= it->usei->serial) {
            int wr = getWrDepBar(it->insn);
            emitWtDepBar(insn, wr);
            bars.clr(wr); // free barrier
            it = live_uses.erase(it);
            continue;
         }
         ++it;
      }

      // Expire old barrier defs.
      for (std::list<LiveBarDef>::iterator it = live_defs.begin();
           it != live_defs.end();) {
         if (insn->serial >= it->defi->serial) {
            int rd = getRdDepBar(it->insn);
            emitWtDepBar(insn, rd);
            bars.clr(rd); // free barrier
            it = live_defs.erase(it);
            continue;
         }
         ++it;
      }

      need_wr_bar = needWrDepBar(insn);
      need_rd_bar = needRdDepBar(insn);

      if (need_wr_bar) {
         // When the instruction requires to emit a write dependency barrier
         // (all which write something at a variable latency), find the next
         // instruction which reads the outputs.
         usei = findFirstUse(insn);

         // Allocate and emit a new barrier.
         bar_id = bars.findFreeRange(1);
         if (bar_id == -1)
            bar_id = 5;
         bars.set(bar_id);
         emitWrDepBar(insn, bar_id);
         if (usei)
            live_uses.push_back(LiveBarUse(insn, usei));
      }

      if (need_rd_bar) {
         // When the instruction requires to emit a read dependency barrier
         // (all which read something at a variable latency), find the next
         // instruction which will write the inputs.
         defi = findFirstDef(insn);

         if (usei && defi && usei->serial <= defi->serial)
            continue;

         // Allocate and emit a new barrier.
         bar_id = bars.findFreeRange(1);
         if (bar_id == -1)
            bar_id = 5;
         bars.set(bar_id);
         emitRdDepBar(insn, bar_id);
         if (defi)
            live_defs.push_back(LiveBarDef(insn, defi));
      }
   }

   // Remove unnecessary barrier waits.
   BitSet alive_bars(6, 1);
   for (insn = bb->getEntry(); insn != NULL; insn = next) {
      int wr, rd, wt;

      next = insn->next;

      wr = getWrDepBar(insn);
      rd = getRdDepBar(insn);
      wt = getWtDepBar(insn);

      for (int idx = 0; idx < 6; ++idx) {
         if (!(wt & (1 << idx)))
            continue;
         if (!alive_bars.test(idx)) {
            insn->sched &= ~(1 << (11  + idx));
         } else {
            alive_bars.clr(idx);
         }
      }

      if (wr < 6)
         alive_bars.set(wr);
      if (rd < 6)
         alive_bars.set(rd);
   }

   return true;
}

bool
SchedDataCalculatorGM107::visit(Function *func)
{
   ArrayList insns;

   func->orderInstructions(insns);

   scoreBoards.resize(func->cfg.getSize());
   for (size_t i = 0; i < scoreBoards.size(); ++i)
      scoreBoards[i].wipe();
   return true;
}

bool
SchedDataCalculatorGM107::visit(BasicBlock *bb)
{
   Instruction *insn, *next = NULL;
   int cycle = 0;

   for (Instruction *insn = bb->getEntry(); insn; insn = insn->next) {
      /*XXX*/
      insn->sched = 0x7e0;
   }

   if (!debug_get_bool_option("NV50_PROG_SCHED", true))
      return true;

   // Insert read/write dependency barriers for instructions which don't
   // operate at a fixed latency.
   insertBarriers(bb);

   score = &scoreBoards.at(bb->getId());

   for (Graph::EdgeIterator ei = bb->cfg.incident(); !ei.end(); ei.next()) {
      // back branches will wait until all target dependencies are satisfied
      if (ei.getType() == Graph::Edge::BACK) // sched would be uninitialized
         continue;
      BasicBlock *in = BasicBlock::get(ei.getNode());
      score->setMax(&scoreBoards.at(in->getId()));
   }

#ifdef GM107_DEBUG_SCHED_DATA
   INFO("=== BB:%i initial scores\n", bb->getId());
   score->print(cycle);
#endif

   // Because barriers are allocated locally (intra-BB), we have to make sure
   // that all produced barriers have been consumed before entering inside a
   // new basic block. The best way is to do a global allocation pre RA but
   // it's really more difficult, especially because of the phi nodes. Anyways,
   // it seems like that waiting on a barrier which has already been consumed
   // doesn't add any additional cost, it's just not elegant!
   Instruction *start = bb->getEntry();
   if (start && bb->cfg.incidentCount() > 0) {
      for (int b = 0; b < 6; b++)
         emitWtDepBar(start, b);
   }

   for (insn = bb->getEntry(); insn && insn->next; insn = insn->next) {
      next = insn->next;

      commitInsn(insn, cycle);
      int delay = calcDelay(next, cycle);
      setDelay(insn, delay, next);
      cycle += getStall(insn);

      setReuseFlag(insn);

      // XXX: The yield flag seems to destroy a bunch of things when it is
      // set on every instruction, need investigation.
      //emitYield(insn);

#ifdef GM107_DEBUG_SCHED_DATA
      printSchedInfo(cycle, insn);
      insn->print();
      next->print();
#endif
   }

   if (!insn)
      return true;
   commitInsn(insn, cycle);

   int bbDelay = -1;

#ifdef GM107_DEBUG_SCHED_DATA
   fprintf(stderr, "last instruction is : ");
   insn->print();
   fprintf(stderr, "cycle=%d\n", cycle);
#endif

   for (Graph::EdgeIterator ei = bb->cfg.outgoing(); !ei.end(); ei.next()) {
      BasicBlock *out = BasicBlock::get(ei.getNode());

      if (ei.getType() != Graph::Edge::BACK) {
         // Only test the first instruction of the outgoing block.
         next = out->getEntry();
         if (next) {
            bbDelay = MAX2(bbDelay, calcDelay(next, cycle));
         } else {
            // When the outgoing BB is empty, make sure to set the number of
            // stall counts needed by the instruction because we don't know the
            // next instruction.
            bbDelay = MAX2(bbDelay, targ->getLatency(insn));
         }
      } else {
         // Wait until all dependencies are satisfied.
         const int regsFree = score->getLatest();
         next = out->getFirst();
         for (int c = cycle; next && c < regsFree; next = next->next) {
            bbDelay = MAX2(bbDelay, calcDelay(next, c));
            c += getStall(next);
         }
         next = NULL;
      }
   }
   if (bb->cfg.outgoingCount() != 1)
      next = NULL;
   setDelay(insn, bbDelay, next);
   cycle += getStall(insn);

   score->rebase(cycle); // common base for initializing out blocks' scores
   return true;
}

/*******************************************************************************
 * main
 ******************************************************************************/

void
CodeEmitterGM107::prepareEmission(Function *func)
{
   SchedDataCalculatorGM107 sched(targGM107);
   CodeEmitter::prepareEmission(func);
   sched.run(func, true, true);
}

static inline uint32_t sizeToBundlesGM107(uint32_t size)
{
   return (size + 23) / 24;
}

void
CodeEmitterGM107::prepareEmission(Program *prog)
{
   for (ArrayList::Iterator fi = prog->allFuncs.iterator();
        !fi.end(); fi.next()) {
      Function *func = reinterpret_cast<Function *>(fi.get());
      func->binPos = prog->binSize;
      prepareEmission(func);

      // adjust sizes & positions for schedulding info:
      if (prog->getTarget()->hasSWSched) {
         uint32_t adjPos = func->binPos;
         BasicBlock *bb = NULL;
         for (int i = 0; i < func->bbCount; ++i) {
            bb = func->bbArray[i];
            int32_t adjSize = bb->binSize;
            if (adjPos % 32) {
               adjSize -= 32 - adjPos % 32;
               if (adjSize < 0)
                  adjSize = 0;
            }
            adjSize = bb->binSize + sizeToBundlesGM107(adjSize) * 8;
            bb->binPos = adjPos;
            bb->binSize = adjSize;
            adjPos += adjSize;
         }
         if (bb)
            func->binSize = adjPos - func->binPos;
      }

      prog->binSize += func->binSize;
   }
}

CodeEmitterGM107::CodeEmitterGM107(const TargetGM107 *target)
   : CodeEmitter(target),
     targGM107(target),
     writeIssueDelays(target->hasSWSched)
{
   code = NULL;
   codeSize = codeSizeLimit = 0;
   relocInfo = NULL;
}

CodeEmitter *
TargetGM107::createCodeEmitterGM107(Program::Type type)
{
   CodeEmitterGM107 *emit = new CodeEmitterGM107(this);
   emit->setProgramType(type);
   return emit;
}

} // namespace nv50_ir
