nvc0/ir: add initial code to support GK110 ISA encoding
authorChristoph Bumiller <e0425955@student.tuwien.ac.at>
Sat, 1 Sep 2012 15:26:24 +0000 (17:26 +0200)
committerChristoph Bumiller <e0425955@student.tuwien.ac.at>
Fri, 7 Sep 2012 17:03:40 +0000 (19:03 +0200)
src/gallium/drivers/nv50/codegen/nv50_ir_driver.h
src/gallium/drivers/nv50/codegen/nv50_ir_inlines.h
src/gallium/drivers/nv50/codegen/nv50_ir_ra.cpp
src/gallium/drivers/nv50/codegen/nv50_ir_target.cpp
src/gallium/drivers/nvc0/Makefile.sources
src/gallium/drivers/nvc0/codegen/nv50_ir_emit_gk110.cpp [new file with mode: 0644]
src/gallium/drivers/nvc0/codegen/nv50_ir_emit_nvc0.cpp
src/gallium/drivers/nvc0/codegen/nv50_ir_lowering_nvc0.cpp
src/gallium/drivers/nvc0/codegen/nv50_ir_target_nvc0.cpp
src/gallium/drivers/nvc0/codegen/nv50_ir_target_nvc0.h

index 9632986fe409d7cb1ca0b82c060e767382531290..446befa5fb78e4cd353b2068010836e9e1cf5ad0 100644 (file)
@@ -93,6 +93,11 @@ struct nv50_ir_prog_symbol
    uint32_t offset;
 };
 
+#define NVISA_GF100_CHIPSET_C0 0xc0
+#define NVISA_GF100_CHIPSET_D0 0xd0
+#define NVISA_GK104_CHIPSET    0xe0
+#define NVISA_GK110_CHIPSET    0xf0
+
 struct nv50_ir_prog_info
 {
    uint16_t target; /* chipset (0x50, 0x84, 0xc0, ...) */
index ab4c98fbcd77659231ba3271917df685b1482094..55a3332c7274cfbe6f2bbeefef96f6dada5ca1e4 100644 (file)
@@ -73,6 +73,31 @@ static inline unsigned int typeSizeof(DataType ty)
    }
 }
 
+static inline unsigned int typeSizeofLog2(DataType ty)
+{
+   switch (ty) {
+   case TYPE_F16:
+   case TYPE_U16:
+   case TYPE_S16:
+      return 1;
+   case TYPE_F32:
+   case TYPE_U32:
+   case TYPE_S32:
+      return 2;
+   case TYPE_F64:
+   case TYPE_U64:
+   case TYPE_S64:
+      return 3;
+   case TYPE_B96:
+   case TYPE_B128:
+      return 4;
+   case TYPE_U8:
+   case TYPE_S8:
+   default:
+      return 0;
+   }
+}
+
 static inline DataType typeOfSize(unsigned int size,
                                   bool flt = false, bool sgn = false)
 {
index 726331e91e7426be1b7395286505eb9fd153a3a1..13998767710ff6e8dd1f740a2018fee12543aecc 100644 (file)
@@ -1907,6 +1907,7 @@ RegAlloc::InsertConstraintsPass::visit(BasicBlock *bb)
             texConstraintNVC0(tex);
             break;
          case 0xe0:
+         case NVISA_GK110_CHIPSET:
             texConstraintNVE0(tex);
             break;
          default:
index f718912fb391abb662e6c4434fe8be64e3c77625..707c9e8d2193a774f6b5c7f77ab1a470d66c5599 100644 (file)
@@ -121,6 +121,7 @@ Target *Target::create(unsigned int chipset)
    case 0xc0:
    case 0xd0:
    case 0xe0:
+   case NVISA_GK110_CHIPSET:
       return getTargetNVC0(chipset);
    case 0x50:
    case 0x80:
index d74ecf385f52d9ef146c8f43978d756645c41903..82504bf9849d20edfa43b3ec29e38f175812dff9 100644 (file)
@@ -16,6 +16,7 @@ C_SOURCES := \
        nvc0_query.c
 
 CPP_SOURCES := \
+       codegen/nv50_ir_emit_gk110.cpp \
        codegen/nv50_ir_emit_nvc0.cpp \
        codegen/nv50_ir_lowering_nvc0.cpp \
        codegen/nv50_ir_target_nvc0.cpp
diff --git a/src/gallium/drivers/nvc0/codegen/nv50_ir_emit_gk110.cpp b/src/gallium/drivers/nvc0/codegen/nv50_ir_emit_gk110.cpp
new file mode 100644 (file)
index 0000000..6c229fd
--- /dev/null
@@ -0,0 +1,1628 @@
+/*
+ * Copyright 2012 Christoph Bumiller
+ *
+ * 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 AUTHORS 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.
+ */
+
+#include "nv50_ir_target_nvc0.h"
+
+// CodeEmitter for GK110 encoding of the Fermi/Kepler ISA.
+
+namespace nv50_ir {
+
+class CodeEmitterGK110 : public CodeEmitter
+{
+public:
+   CodeEmitterGK110(const TargetNVC0 *);
+
+   virtual bool emitInstruction(Instruction *);
+   virtual uint32_t getMinEncodingSize(const Instruction *) const;
+   virtual void prepareEmission(Function *);
+
+   inline void setProgramType(Program::Type pType) { progType = pType; }
+
+private:
+   const TargetNVC0 *targ;
+
+   Program::Type progType;
+
+   const bool writeIssueDelays;
+
+private:
+   void emitForm_21(const Instruction *, uint32_t opc2, uint32_t opc1);
+   void emitForm_C(const Instruction *, uint32_t opc, uint8_t ctg);
+   void emitForm_L(const Instruction *, uint32_t opc, uint8_t ctg, Modifier);
+
+   void emitPredicate(const Instruction *);
+
+   void setCAddress14(const ValueRef&);
+   void setShortImmediate(const Instruction *, const int s);
+   void setImmediate32(const Instruction *, const int s, Modifier);
+
+   void modNegAbsF32_3b(const Instruction *, const int s);
+
+   void emitCondCode(CondCode cc, int pos, uint8_t mask);
+   void emitInterpMode(const Instruction *);
+   void emitLoadStoreType(DataType ty, const int pos);
+   void emitCachingMode(CacheMode c, const int pos);
+
+   inline uint8_t getSRegEncoding(const ValueRef&);
+
+   void emitRoundMode(RoundMode, const int pos, const int rintPos);
+   void emitRoundModeF(RoundMode, const int pos);
+   void emitRoundModeI(RoundMode, const int pos);
+
+   void emitNegAbs12(const Instruction *);
+
+   void emitNOP(const Instruction *);
+
+   void emitLOAD(const Instruction *);
+   void emitSTORE(const Instruction *);
+   void emitMOV(const Instruction *);
+
+   void emitINTERP(const Instruction *);
+   void emitPFETCH(const Instruction *);
+   void emitVFETCH(const Instruction *);
+   void emitEXPORT(const Instruction *);
+   void emitOUT(const Instruction *);
+
+   void emitUADD(const Instruction *);
+   void emitFADD(const Instruction *);
+   void emitIMUL(const Instruction *);
+   void emitFMUL(const Instruction *);
+   void emitIMAD(const Instruction *);
+   void emitISAD(const Instruction *);
+   void emitFMAD(const Instruction *);
+
+   void emitNOT(const Instruction *);
+   void emitLogicOp(const Instruction *, uint8_t subOp);
+   void emitPOPC(const Instruction *);
+   void emitINSBF(const Instruction *);
+   void emitShift(const Instruction *);
+
+   void emitSFnOp(const Instruction *, uint8_t subOp);
+
+   void emitCVT(const Instruction *);
+   void emitMINMAX(const Instruction *);
+   void emitPreOp(const Instruction *);
+
+   void emitSET(const CmpInstruction *);
+   void emitSLCT(const CmpInstruction *);
+   void emitSELP(const Instruction *);
+
+   void emitTEXBAR(const Instruction *);
+   void emitTEX(const TexInstruction *);
+   void emitTEXCSAA(const TexInstruction *);
+   void emitTXQ(const TexInstruction *);
+   void emitPIXLD(const TexInstruction *);
+
+   void emitQUADOP(const Instruction *, uint8_t qOp, uint8_t laneMask);
+
+   void emitFlow(const Instruction *);
+
+   inline void defId(const ValueDef&, const int pos);
+   inline void srcId(const ValueRef&, const int pos);
+   inline void srcId(const ValueRef *, const int pos);
+   inline void srcId(const Instruction *, int s, const int pos);
+
+   inline void srcAddr32(const ValueRef&, const int pos); // address / 4
+
+   inline bool isLIMM(const ValueRef&, DataType ty, bool mod = false);
+};
+
+#define GK110_GPR_ZERO 255
+
+#define NEG_(b, s) \
+   if (i->src(s).mod.neg()) code[(0x##b) / 32] |= 1 << ((0x##b) % 32)
+#define ABS_(b, s) \
+   if (i->src(s).mod.abs()) code[(0x##b) / 32] |= 1 << ((0x##b) % 32)
+
+#define NOT_(b, s) if (i->src(s).mod & Modifier(NV50_IR_MOD_NOT))       \
+   code[(0x##b) / 32] |= 1 << ((0x##b) % 32)
+
+#define FTZ_(b) if (i->ftz) code[(0x##b) / 32] |= 1 << ((0x##b) % 32)
+
+#define SAT_(b) if (i->saturate) code[(0x##b) / 32] |= 1 << ((0x##b) % 32)
+
+#define RND_(b, t) emitRoundMode##t(i->rnd, 0x##b)
+
+#define SDATA(a) ((a).rep()->reg.data)
+#define DDATA(a) ((a).rep()->reg.data)
+
+void CodeEmitterGK110::srcId(const ValueRef& src, const int pos)
+{
+   code[pos / 32] |= (src.get() ? SDATA(src).id : GK110_GPR_ZERO) << (pos % 32);
+}
+
+void CodeEmitterGK110::srcId(const ValueRef *src, const int pos)
+{
+   code[pos / 32] |= (src ? SDATA(*src).id : GK110_GPR_ZERO) << (pos % 32);
+}
+
+void CodeEmitterGK110::srcId(const Instruction *insn, int s, int pos)
+{
+   int r = insn->srcExists(s) ? SDATA(insn->src(s)).id : GK110_GPR_ZERO;
+   code[pos / 32] |= r << (pos % 32);
+}
+
+void CodeEmitterGK110::srcAddr32(const ValueRef& src, const int pos)
+{
+   code[pos / 32] |= (SDATA(src).offset >> 2) << (pos % 32);
+}
+
+void CodeEmitterGK110::defId(const ValueDef& def, const int pos)
+{
+   code[pos / 32] |= (def.get() ? DDATA(def).id : GK110_GPR_ZERO) << (pos % 32);
+}
+
+bool CodeEmitterGK110::isLIMM(const ValueRef& ref, DataType ty, bool mod)
+{
+   const ImmediateValue *imm = ref.get()->asImm();
+
+   return imm && (imm->reg.data.u32 & ((ty == TYPE_F32) ? 0xfff : 0xfff00000));
+}
+
+void
+CodeEmitterGK110::emitRoundMode(RoundMode rnd, const int pos, const int rintPos)
+{
+   bool rint = false;
+   uint8_t n;
+
+   switch (rnd) {
+   case ROUND_MI: rint = true; /* fall through */ case ROUND_M: n = 1; break;
+   case ROUND_PI: rint = true; /* fall through */ case ROUND_P: n = 2; break;
+   case ROUND_ZI: rint = true; /* fall through */ case ROUND_Z: n = 3; break;
+   default:
+      rint = rnd == ROUND_NI;
+      n = 0;
+      assert(rnd == ROUND_N || rnd == ROUND_NI);
+      break;
+   }
+   code[pos / 32] |= n << (pos % 32);
+   if (rint && rintPos >= 0)
+      code[rintPos / 32] |= 1 << (rintPos % 32);
+}
+
+void
+CodeEmitterGK110::emitRoundModeF(RoundMode rnd, const int pos)
+{
+   uint8_t n;
+
+   switch (rnd) {
+   case ROUND_M: n = 1; break;
+   case ROUND_P: n = 2; break;
+   case ROUND_Z: n = 3; break;
+   default:
+      n = 0;
+      assert(rnd == ROUND_N);
+      break;
+   }
+   code[pos / 32] |= n << (pos % 32);
+}
+
+void
+CodeEmitterGK110::emitRoundModeI(RoundMode rnd, const int pos)
+{
+   uint8_t n;
+
+   switch (rnd) {
+   case ROUND_MI: n = 1; break;
+   case ROUND_PI: n = 2; break;
+   case ROUND_ZI: n = 3; break;
+   default:
+      n = 0;
+      assert(rnd == ROUND_NI);
+      break;
+   }
+   code[pos / 32] |= n << (pos % 32);
+}
+
+void CodeEmitterGK110::emitCondCode(CondCode cc, int pos, uint8_t mask)
+{
+   uint8_t n;
+
+   switch (cc) {
+   case CC_FL:  n = 0x00; break;
+   case CC_LT:  n = 0x01; break;
+   case CC_EQ:  n = 0x02; break;
+   case CC_LE:  n = 0x03; break;
+   case CC_GT:  n = 0x04; break;
+   case CC_NE:  n = 0x05; break;
+   case CC_GE:  n = 0x06; break;
+   case CC_LTU: n = 0x09; break;
+   case CC_EQU: n = 0x0a; break;
+   case CC_LEU: n = 0x0b; break;
+   case CC_GTU: n = 0x0c; break;
+   case CC_NEU: n = 0x0d; break;
+   case CC_GEU: n = 0x0e; break;
+   case CC_TR:  n = 0x0f; break;
+   case CC_NO:  n = 0x10; break;
+   case CC_NC:  n = 0x11; break;
+   case CC_NS:  n = 0x12; break;
+   case CC_NA:  n = 0x13; break;
+   case CC_A:   n = 0x14; break;
+   case CC_S:   n = 0x15; break;
+   case CC_C:   n = 0x16; break;
+   case CC_O:   n = 0x17; break;
+   default:
+      n = 0;
+      assert(!"invalid condition code");
+      break;
+   }
+   code[pos / 32] |= (n & mask) << (pos % 32);
+}
+
+void
+CodeEmitterGK110::emitPredicate(const Instruction *i)
+{
+   if (i->predSrc >= 0) {
+      srcId(i->src(i->predSrc), 18);
+      if (i->cc == CC_NOT_P)
+         code[0] |= 8 << 18; // negate
+      assert(i->getPredicate()->reg.file == FILE_PREDICATE);
+   } else {
+      code[0] |= 7 << 18;
+   }
+}
+
+void
+CodeEmitterGK110::setCAddress14(const ValueRef& src)
+{
+   const int32_t addr = src.get()->asSym()->reg.data.offset / 4;
+
+   code[0] |= (addr & 0x01ff) << 23;
+   code[1] |= (addr & 0x3e00) >> 9;
+}
+
+void
+CodeEmitterGK110::setShortImmediate(const Instruction *i, const int s)
+{
+   const uint32_t u32 = i->getSrc(s)->asImm()->reg.data.u32;
+   const uint64_t u64 = i->getSrc(s)->asImm()->reg.data.u64;
+
+   if (i->sType == TYPE_F32) {
+      assert(!(u32 & 0x00000fff));
+      code[0] |= ((u32 & 0x001ff000) >> 12) << 23;
+      code[1] |= ((u32 & 0x7fe00000) >> 21);
+      code[1] |= ((u32 & 0x80000000) >> 4);
+   } else
+   if (i->sType == TYPE_F64) {
+      assert(!(u64 & 0x00000fffffffffffULL));
+      code[0] |= ((u64 & 0x001ff00000000000ULL) >> 44) << 23;
+      code[1] |= ((u64 & 0x7fe0000000000000ULL) >> 53);
+      code[1] |= ((u64 & 0x8000000000000000ULL) >> 36);
+   } else {
+      assert((u32 & 0xfff00000) == 0 || (u32 & 0xfff00000) == 0xfff00000);
+      code[0] |= (u32 & 0x001ff) << 23;
+      code[1] |= (u32 & 0x7fe00) >> 9;
+      code[1] |= (u32 & 0x80000) << 8;
+   }
+}
+
+void
+CodeEmitterGK110::setImmediate32(const Instruction *i, const int s,
+                                 Modifier mod)
+{
+   uint32_t u32 = i->getSrc(s)->asImm()->reg.data.u32;
+
+   if (mod) {
+      ImmediateValue imm(i->getSrc(s)->asImm(), i->sType);
+      mod.applyTo(imm);
+      u32 = imm.reg.data.u32;
+   }
+
+   code[0] |= u32 << 23;
+   code[1] |= u32 >> 9;
+}
+
+void
+CodeEmitterGK110::emitForm_L(const Instruction *i, uint32_t opc, uint8_t ctg,
+                             Modifier mod)
+{
+   code[0] = ctg;
+   code[1] = opc << 20;
+
+   emitPredicate(i);
+
+   defId(i->def(0), 2);
+
+   for (int s = 0; s < 3 && i->srcExists(s); ++s) {
+      switch (i->src(s).getFile()) {
+      case FILE_GPR:
+         srcId(i->src(s), s ? 42 : 10);
+         break;
+      case FILE_IMMEDIATE:
+         setImmediate32(i, s, mod);
+         break;
+      default:
+         break;
+      }
+   }
+}
+
+
+void
+CodeEmitterGK110::emitForm_C(const Instruction *i, uint32_t opc, uint8_t ctg)
+{
+   code[0] = ctg;
+   code[1] = opc << 20;
+
+   emitPredicate(i);
+
+   defId(i->def(0), 2);
+
+   switch (i->src(0).getFile()) {
+   case FILE_MEMORY_CONST:
+      code[1] |= 0x4 << 28;
+      setCAddress14(i->src(0));
+      break;
+   case FILE_GPR:
+      code[1] |= 0xc << 28;
+      srcId(i->src(0), 23);
+      break;
+   default:
+      assert(0);
+      break;
+   }
+}
+
+// 0x2 for GPR, c[] and 0x1 for short immediate
+void
+CodeEmitterGK110::emitForm_21(const Instruction *i, uint32_t opc2,
+                              uint32_t opc1)
+{
+   const bool imm = i->srcExists(1) && i->src(1).getFile() == FILE_IMMEDIATE;
+
+   int s1 = 23;
+   if (i->srcExists(2) && i->src(2).getFile() == FILE_MEMORY_CONST)
+      s1 = 42;
+
+   if (imm) {
+      code[0] = 0x1;
+      code[1] = opc1 << 20;
+   } else {
+      code[0] = 0x2;
+      code[1] = (0xc << 28) | (opc2 << 20);
+   }
+
+   emitPredicate(i);
+
+   defId(i->def(0), 2);
+
+   for (int s = 0; s < 3 && i->srcExists(s); ++s) {
+      switch (i->src(s).getFile()) {
+      case FILE_MEMORY_CONST:
+         code[1] &= (s == 2) ? ~(0x4 << 28) : ~(0x8 << 28);
+         setCAddress14(i->src(s));
+         code[1] |= i->getSrc(s)->reg.fileIndex << 5;
+         break;
+      case FILE_IMMEDIATE:
+         setShortImmediate(i, s);
+         break;
+      case FILE_GPR:
+         srcId(i->src(s), s ? ((s == 2) ? 42 : s1) : 10);
+         break;
+      default:
+         // ignore here, can be predicate or flags, but must not be address
+         break;
+      }
+   }
+   // 0x0 = invalid
+   // 0xc = rrr
+   // 0x8 = rrc
+   // 0x4 = rcr
+   assert(imm || (code[1] & (0xc << 28)));
+}
+
+inline void
+CodeEmitterGK110::modNegAbsF32_3b(const Instruction *i, const int s)
+{
+   if (i->src(s).mod.abs()) code[1] &= ~(1 << 27);
+   if (i->src(s).mod.neg()) code[1] ^=  (1 << 27);
+}
+
+void
+CodeEmitterGK110::emitNOP(const Instruction *i)
+{
+   code[0] = 0x00003c02;
+   code[1] = 0x85800000;
+
+   if (i)
+      emitPredicate(i);
+   else
+      code[0] = 0x001c3c02;
+}
+
+void
+CodeEmitterGK110::emitFMAD(const Instruction *i)
+{
+   assert(!isLIMM(i->src(1), TYPE_F32));
+
+   emitForm_21(i, 0x0c0, 0x940);
+
+   NEG_(34, 2);
+   SAT_(35);
+   RND_(36, F);
+   FTZ_(38);
+
+   bool neg1 = (i->src(0).mod ^ i->src(1).mod).neg();
+
+   if (code[0] & 0x1) {
+      if (neg1)
+         code[1] ^= 1 << 27;
+   } else
+   if (neg1) {
+      code[1] |= 1 << 19;
+   }
+}
+
+void
+CodeEmitterGK110::emitFMUL(const Instruction *i)
+{
+   bool neg = (i->src(0).mod ^ i->src(1).mod).neg();
+
+   assert(i->postFactor >= -3 && i->postFactor <= 3);
+
+   if (isLIMM(i->src(1), TYPE_F32)) {
+      emitForm_L(i, 0x200, 0x2, Modifier(0));
+
+      FTZ_(38);
+      SAT_(3a);
+      if (neg)
+         code[1] ^= 1 << 22;
+
+      assert(i->postFactor == 0);
+   } else {
+      emitForm_21(i, 0x234, 0xc34);
+
+      RND_(2a, F);
+      FTZ_(2f);
+      SAT_(35);
+
+      if (code[0] & 0x1) {
+         if (neg)
+            code[1] ^= 1 << 27;
+      } else
+      if (neg) {
+         code[1] |= 1 << 19;
+      }
+   }
+}
+
+void
+CodeEmitterGK110::emitIMUL(const Instruction *i)
+{
+   assert(!i->src(0).mod.neg() && !i->src(1).mod.neg());
+   assert(!i->src(0).mod.abs() && !i->src(1).mod.abs());
+
+   if (isLIMM(i->src(1), TYPE_S32)) {
+      emitForm_L(i, 0x280, 2, Modifier(0));
+
+      assert(i->subOp != NV50_IR_SUBOP_MUL_HIGH);
+
+      if (i->sType == TYPE_S32)
+         code[1] |= 3 << 25;
+   } else {
+      emitForm_21(i, 0x21c, 0xc1c);
+
+      if (i->subOp == NV50_IR_SUBOP_MUL_HIGH)
+         code[1] |= 1 << 10;
+      if (i->sType == TYPE_S32)
+         code[1] |= 3 << 11;
+   }
+}
+
+void
+CodeEmitterGK110::emitFADD(const Instruction *i)
+{
+   if (isLIMM(i->src(1), TYPE_F32)) {
+      assert(i->rnd == ROUND_N);
+      assert(!i->saturate);
+
+      emitForm_L(i, 0x400, 0, i->src(1).mod);
+
+      FTZ_(3a);
+      NEG_(3b, 0);
+      ABS_(39, 0);
+   } else {
+      emitForm_21(i, 0x22c, 0xc2c);
+
+      FTZ_(2f);
+      RND_(2a, F);
+      ABS_(31, 0);
+      NEG_(33, 0);
+
+      if (code[0] & 0x1) {
+         modNegAbsF32_3b(i, 1);
+      } else {
+         ABS_(34, 1);
+         NEG_(30, 1);
+      }
+   }
+}
+
+void
+CodeEmitterGK110::emitUADD(const Instruction *i)
+{
+   uint8_t addOp = (i->src(0).mod.neg() << 1) | i->src(1).mod.neg();
+
+   if (i->op == OP_SUB)
+      addOp ^= 1;
+
+   assert(!i->src(0).mod.abs() && !i->src(1).mod.abs());
+
+   if (isLIMM(i->src(1), TYPE_S32)) {
+      emitForm_L(i, 0x400, 1, Modifier((addOp & 1) ? NV50_IR_MOD_NEG : 0));
+
+      if (addOp & 2)
+         code[1] |= 1 << 27;
+
+      assert(!i->defExists(1));
+      assert(i->flagsSrc < 0);
+
+      SAT_(39);
+   } else {
+      emitForm_21(i, 0x208, 0xc08);
+
+      assert(addOp != 3); // would be add-plus-one
+
+      code[1] |= addOp << 19;
+
+      if (i->defExists(1))
+         code[1] |= 1 << 18; // write carry
+      if (i->flagsSrc >= 0)
+         code[1] |= 1 << 14; // add carry
+
+      SAT_(35);
+   }
+}
+
+// TODO: shl-add
+void
+CodeEmitterGK110::emitIMAD(const Instruction *i)
+{
+   uint8_t addOp =
+      (i->src(2).mod.neg() << 1) | (i->src(0).mod.neg() ^ i->src(1).mod.neg());
+
+   emitForm_21(i, 0x100, 0xa00);
+
+   assert(addOp != 3);
+   code[1] |= addOp << 26;
+
+   if (i->sType == TYPE_S32)
+      code[1] |= (1 << 19) | (1 << 24);
+
+   if (code[0] & 0x1) {
+      assert(!i->subOp);
+      SAT_(39);
+   } else {
+      if (i->subOp == NV50_IR_SUBOP_MUL_HIGH)
+         code[1] |= 1 << 25;
+      SAT_(35);
+   }
+}
+
+void
+CodeEmitterGK110::emitISAD(const Instruction *i)
+{
+   assert(i->dType == TYPE_S32 || i->dType == TYPE_U32);
+
+   emitForm_21(i, 0x1fc, 0xb74);
+
+   if (i->dType == TYPE_S32)
+      code[1] |= 1 << 19;
+}
+
+void
+CodeEmitterGK110::emitNOT(const Instruction *i)
+{
+   code[0] = 0x0003fc02; // logop(mov2) dst, 0, not src
+   code[1] = 0x22003800;
+
+   emitPredicate(i);
+
+   defId(i->def(0), 2);
+
+   switch (i->src(0).getFile()) {
+   case FILE_GPR:
+      code[1] |= 0xc << 28;
+      srcId(i->src(0), 23);
+      break;
+   case FILE_MEMORY_CONST:
+      code[1] |= 0x4 << 28;
+      setCAddress14(i->src(1));
+      break;
+   default:
+      assert(0);
+      break;
+   }
+}
+
+void
+CodeEmitterGK110::emitLogicOp(const Instruction *i, uint8_t subOp)
+{
+   assert(!(i->src(0).mod & Modifier(NV50_IR_MOD_NOT))); // XXX: find me
+
+   if (isLIMM(i->src(1), TYPE_S32)) {
+      emitForm_L(i, 0x200, 0, i->src(1).mod);
+      code[1] |= subOp << 24;
+   } else {
+      emitForm_21(i, 0x220, 0xc20);
+      code[1] |= subOp << 12;
+      NOT_(2b, 1);
+   }
+   assert(!(code[0] & 0x1) || !(i->src(1).mod & Modifier(NV50_IR_MOD_NOT)));
+}
+
+void
+CodeEmitterGK110::emitPOPC(const Instruction *i)
+{
+   assert(!isLIMM(i->src(1), TYPE_S32, true));
+
+   emitForm_21(i, 0x204, 0xc04);
+
+   NOT_(2a, 0);
+   if (!(code[0] & 0x1))
+      NOT_(2b, 1);
+}
+
+void
+CodeEmitterGK110::emitINSBF(const Instruction *i)
+{
+   emitForm_21(i, 0x1f8, 0xb78);
+}
+
+void
+CodeEmitterGK110::emitShift(const Instruction *i)
+{
+   const bool sar = i->op == OP_SHR && isSignedType(i->sType);
+
+   if (sar) {
+      emitForm_21(i, 0x214, 0x014);
+      code[1] |= 1 << 19;
+   } else
+   if (i->op == OP_SHR) {
+      // this is actually RSHF
+      emitForm_21(i, 0x27c, 0x87c);
+      code[1] |= GK110_GPR_ZERO << 10;
+   } else {
+      // this is actually LSHF
+      emitForm_21(i, 0x1fc, 0xb7c);
+      code[1] |= GK110_GPR_ZERO << 10;
+   }
+
+   if (i->subOp == NV50_IR_SUBOP_SHIFT_WRAP) {
+      if (!sar)
+         code[1] |= 1 << 21;
+      // XXX: find wrap modifier for SHR S32
+   }
+}
+
+void
+CodeEmitterGK110::emitPreOp(const Instruction *i)
+{
+   emitForm_21(i, 0x248, -1);
+
+   if (i->op == OP_PREEX2)
+      code[1] |= 1 << 10;
+
+   NEG_(30, 0);
+   ABS_(34, 0);
+}
+
+void
+CodeEmitterGK110::emitSFnOp(const Instruction *i, uint8_t subOp)
+{
+   code[0] = 0x00000002 | (subOp << 23);
+   code[1] = 0x84000000;
+
+   emitPredicate(i);
+
+   defId(i->def(0), 2);
+   srcId(i->src(0), 10);
+
+   NEG_(33, 0);
+   ABS_(31, 0);
+
+   // XXX: find saturate
+}
+
+void
+CodeEmitterGK110::emitMINMAX(const Instruction *i)
+{
+   uint32_t op2, op1;
+
+   switch (i->dType) {
+   case TYPE_U32:
+   case TYPE_S32:
+      op2 = 0x210;
+      op1 = 0xc10;
+      break;
+   case TYPE_F32:
+      op2 = 0x230;
+      op1 = 0xc30;
+      break;
+   case TYPE_F64:
+      op2 = 0x228;
+      op1 = 0xc28;
+      break;
+   default:
+      assert(0);
+      op2 = 0;
+      op1 = 0;
+      break;
+   }
+   emitForm_21(i, op2, op1);
+
+   if (i->dType == TYPE_S32)
+      code[1] |= 1 << 19;
+   code[1] |= (i->op == OP_MIN) ? 0x1c00 : 0x3c00; // [!]pt
+
+   FTZ_(2f);
+   ABS_(31, 0);
+   NEG_(33, 0);
+   if (code[0] & 0x1) {
+      modNegAbsF32_3b(i, 1);
+   } else {
+      ABS_(34, 1);
+      NEG_(30, 1);
+   }
+}
+
+void
+CodeEmitterGK110::emitCVT(const Instruction *i)
+{
+   const bool f2f = isFloatType(i->dType) && isFloatType(i->sType);
+   const bool f2i = !isFloatType(i->dType) && isFloatType(i->sType);
+   const bool i2f = isFloatType(i->dType) && !isFloatType(i->sType);
+
+   bool sat = i->saturate;
+   bool abs = i->src(0).mod.abs();
+   bool neg = i->src(0).mod.neg();
+
+   RoundMode rnd = i->rnd;
+
+   switch (i->op) {
+   case OP_CEIL:  rnd = f2f ? ROUND_PI : ROUND_P; break;
+   case OP_FLOOR: rnd = f2f ? ROUND_MI : ROUND_M; break;
+   case OP_TRUNC: rnd = f2f ? ROUND_ZI : ROUND_Z; break;
+   case OP_SAT: sat = true; break;
+   case OP_NEG: neg = !neg; break;
+   case OP_ABS: abs = true; neg = false; break;
+   default:
+      break;
+   }
+
+   uint32_t op;
+
+   if      (f2f) op = 0x254;
+   else if (f2i) op = 0x258;
+   else if (i2f) op = 0x25c;
+   else          op = 0x260;
+
+   emitForm_C(i, op, 0x2);
+
+   FTZ_(2f);
+   if (neg) code[1] |= 1 << 16;
+   if (abs) code[1] |= 1 << 20;
+   if (sat) code[1] |= 1 << 21;
+
+   emitRoundMode(rnd, 32 + 10, f2f ? (32 + 13) : -1);
+
+   code[0] |= typeSizeofLog2(i->dType) << 10;
+   code[0] |= typeSizeofLog2(i->sType) << 12;
+
+   if (isSignedIntType(i->dType))
+      code[0] |= 0x4000;
+   if (isSignedIntType(i->sType))
+      code[0] |= 0x8000;
+}
+
+void
+CodeEmitterGK110::emitSET(const CmpInstruction *i)
+{
+   uint16_t op1, op2;
+
+   if (i->def(0).getFile() == FILE_PREDICATE) {
+      switch (i->sType) {
+      case TYPE_F32: op2 = 0x1d8; op1 = 0xb58; break;
+      case TYPE_F64: op2 = 0x1c0; op1 = 0xb40; break;
+      default:
+         op2 = 0x1b0;
+         op1 = 0xb30;
+         break;
+      }
+      emitForm_21(i, op2, op1);
+
+      NEG_(2e, 0);
+      ABS_(9, 0);
+      if (!(code[0] & 0x1)) {
+         NEG_(8, 1);
+         ABS_(2f, 1);
+      } else {
+         modNegAbsF32_3b(i, 1);
+      }
+      FTZ_(32);
+
+      // normal DST field is negated predicate result
+      code[0] = (code[0] & ~0xfc) | ((code[0] << 3) & 0xe0);
+      if (i->defExists(1))
+         defId(i->def(1), 2);
+   else
+      code[0] |= 0x1c;
+   } else {
+      switch (i->sType) {
+      case TYPE_F32: op2 = 0x000; op1 = 0x820; break;
+      case TYPE_F64: op2 = 0x080; op1 = 0x900; break;
+      default:
+         op2 = 0x1a8;
+         op1 = 0xb28;
+         break;
+      }
+      emitForm_21(i, op2, op1);
+
+      NEG_(2e, 0);
+      ABS_(39, 0);
+      if (!(code[0] & 0x1)) {
+         NEG_(38, 1);
+         ABS_(2f, 1);
+      } else {
+         modNegAbsF32_3b(i, 1);
+      }
+      FTZ_(3a);
+   }
+   if (i->sType == TYPE_S32)
+      code[1] |= 1 << 19;
+
+   if (i->op != OP_SET) {
+      switch (i->op) {
+      case OP_SET_AND: code[1] |= 0x0 << 16; break;
+      case OP_SET_OR:  code[1] |= 0x1 << 16; break;
+      case OP_SET_XOR: code[1] |= 0x2 << 16; break;
+      default:
+         assert(0);
+         break;
+      }
+      srcId(i->src(2), 0x2a);
+   } else {
+      code[1] |= 0x7 << 10;
+   }
+   emitCondCode(i->setCond,
+                isFloatType(i->sType) ? 0x33 : 0x34,
+                isFloatType(i->sType) ? 0xf : 0x7);
+}
+
+void
+CodeEmitterGK110::emitSLCT(const CmpInstruction *i)
+{
+   CondCode cc = i->setCond;
+   if (i->src(2).mod.neg())
+      cc = reverseCondCode(cc);
+
+   if (i->dType == TYPE_F32) {
+      emitForm_21(i, 0x1d0, 0xb50);
+      FTZ_(32);
+      emitCondCode(cc, 0x33, 0xf);
+   } else {
+      emitForm_21(i, 0x1a4, 0xb20);
+      emitCondCode(cc, 0x34, 0x7);
+   }
+}
+
+void CodeEmitterGK110::emitSELP(const Instruction *i)
+{
+   emitForm_21(i, 0x250, 0x050);
+
+   if ((i->cc == CC_NOT_P) ^ (bool)(i->src(2).mod & Modifier(NV50_IR_MOD_NOT)))
+      code[1] |= 1 << 13;
+}
+
+void CodeEmitterGK110::emitTEXBAR(const Instruction *i)
+{
+   code[0] = 0x00000002 | (i->subOp << 23);
+   code[1] = 0x77000000;
+
+   emitPredicate(i);
+}
+
+void CodeEmitterGK110::emitTEXCSAA(const TexInstruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+static inline bool
+isNextIndependentTex(const TexInstruction *i)
+{
+   if (!i->next || !isTextureOp(i->next->op))
+      return false;
+   if (i->getDef(0)->interfers(i->next->getSrc(0)))
+      return false;
+   return !i->next->srcExists(1) || !i->getDef(0)->interfers(i->next->getSrc(1));
+}
+
+void
+CodeEmitterGK110::emitTEX(const TexInstruction *i)
+{
+   const bool ind = i->tex.rIndirectSrc >= 0;
+
+   if (ind) {
+      code[0] = 0x00000002;
+      switch (i->op) {
+      case OP_TXD:
+         code[1] = 0x7e000000;
+         break;
+      default:
+         code[1] = 0x7d800000;
+         break;
+      }
+   } else {
+      switch (i->op) {
+      case OP_TXD:
+         code[0] = 0x00000002;
+         code[1] = 0x76000000;
+         break;
+      default:
+         code[0] = 0x00000001;
+         code[1] = 0x60000000;
+         break;
+      }
+      code[1] |= i->tex.r << 15;
+   }
+
+   code[1] |= isNextIndependentTex(i) ? 0x1 : 0x2; // t : p mode
+
+   // if (i->tex.liveOnly)
+   //    ?
+
+   switch (i->op) {
+   case OP_TEX: break;
+   case OP_TXB: code[1] |= 0x2000; break;
+   case OP_TXL: code[1] |= 0x3000; break;
+   case OP_TXF: break; // XXX
+   case OP_TXG: break; // XXX
+   case OP_TXD: break;
+   default:
+      assert(!"invalid texture op");
+      break;
+   }
+   /*
+   if (i->op == OP_TXF) {
+      if (!i->tex.levelZero)
+         code[1] |= 0x02000000;
+   } else */
+   if (i->tex.levelZero) {
+      code[1] |= 0x1000;
+   }
+
+   // if (i->op != OP_TXD && i->tex.derivAll)
+   //   code[1] |= 1 << 13;
+
+   emitPredicate(i);
+
+   code[1] |= i->tex.mask << 2;
+
+   const int src1 = (i->predSrc == 1) ? 2 : 1; // if predSrc == 1, !srcExists(2)
+
+   defId(i->def(0), 2);
+   srcId(i->src(0), 10);
+   srcId(i, src1, 23);
+
+   // if (i->op == OP_TXG) code[0] |= i->tex.gatherComp << 5;
+
+   // texture target:
+   code[1] |= (i->tex.target.isCube() ? 3 : (i->tex.target.getDim() - 1)) << 7;
+   if (i->tex.target.isArray())
+      code[1] |= 0x40;
+   // if (i->tex.target.isShadow())
+   //   ?
+   // if (i->tex.target == TEX_TARGET_2D_MS ||
+   //     i->tex.target == TEX_TARGET_2D_MS_ARRAY)
+   //   ?
+
+   if (i->srcExists(src1) && i->src(src1).getFile() == FILE_IMMEDIATE) {
+      // ?
+   }
+
+   // if (i->tex.useOffsets)
+   //   ?
+}
+
+void
+CodeEmitterGK110::emitTXQ(const TexInstruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitQUADOP(const Instruction *i, uint8_t qOp, uint8_t laneMask)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitFlow(const Instruction *i)
+{
+   const FlowInstruction *f = i->asFlow();
+
+   unsigned mask; // bit 0: predicate, bit 1: target
+
+   code[0] = 0x00000000;
+
+   switch (i->op) {
+   case OP_BRA:
+      code[1] = f->absolute ? 0x00000 : 0x12000000; // XXX
+      // if (i->srcExists(0) && i->src(0).getFile() == FILE_MEMORY_CONST)
+      //   code[0] |= 0x4000;
+      mask = 3;
+      break;
+   case OP_CALL:
+      code[1] = f->absolute ? 0x00000 : 0x13000000; // XXX
+      // if (i->srcExists(0) && i->src(0).getFile() == FILE_MEMORY_CONST)
+      //   code[0] |= 0x4000;
+      mask = 2;
+      break;
+
+   case OP_EXIT:    code[1] = 0x18000000; mask = 1; break;
+   case OP_RET:     code[1] = 0x19000000; mask = 1; break;
+   case OP_DISCARD: code[1] = 0x19800000; mask = 1; break; // XXX: guess
+   case OP_BREAK:   code[1] = 0x1a800000; mask = 1; break; // XXX: guess
+   case OP_CONT:    code[1] = 0x1b000000; mask = 1; break; // XXX: guess
+
+   case OP_JOINAT:   code[1] = 0x14800000; mask = 2; break;
+   case OP_PREBREAK: code[1] = 0x15000000; mask = 2; break; // XXX: guess
+   case OP_PRECONT:  code[1] = 0x15800000; mask = 2; break; // XXX: guess
+   case OP_PRERET:   code[1] = 0x16000000; mask = 2; break; // XXX: guess
+
+   case OP_QUADON:  code[1] = 0x1c000000; mask = 0; break; // XXX: guess
+   case OP_QUADPOP: code[1] = 0x1c800000; mask = 0; break; // XXX: guess
+   case OP_BRKPT:   code[1] = 0x1d000000; mask = 0; break; // XXX: guess
+   default:
+      assert(!"invalid flow operation");
+      return;
+   }
+
+   if (mask & 1) {
+      emitPredicate(i);
+      if (i->flagsSrc < 0)
+         code[0] |= 0x3c;
+   }
+
+   if (!f)
+      return;
+
+   // TODO
+   /*
+   if (f->allWarp)
+      code[0] |= 1 << 15;
+   if (f->limit)
+      code[0] |= 1 << 16;
+   */
+
+   if (f->op == OP_CALL) {
+      if (f->builtin) {
+         assert(f->absolute);
+         uint32_t pcAbs = targ->getBuiltinOffset(f->target.builtin);
+         addReloc(RelocEntry::TYPE_BUILTIN, 0, pcAbs, 0xff800000, 23);
+         addReloc(RelocEntry::TYPE_BUILTIN, 1, pcAbs, 0x007fffff, -9);
+      } else {
+         assert(!f->absolute);
+         int32_t pcRel = f->target.fn->binPos - (codeSize + 8);
+         code[0] |= (pcRel & 0x1ff) << 23;
+         code[1] |= (pcRel >> 9) & 0x7fff;
+      }
+   } else
+   if (mask & 2) {
+      int32_t pcRel = f->target.bb->binPos - (codeSize + 8);
+      // currently we don't want absolute branches
+      assert(!f->absolute);
+      code[0] |= (pcRel & 0x1ff) << 23;
+      code[1] |= (pcRel >> 9) & 0x7fff;
+   }
+}
+
+void
+CodeEmitterGK110::emitPFETCH(const Instruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitVFETCH(const Instruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitEXPORT(const Instruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitOUT(const Instruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitInterpMode(const Instruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitINTERP(const Instruction *i)
+{
+   emitNOP(i); // TODO
+}
+
+void
+CodeEmitterGK110::emitLoadStoreType(DataType ty, const int pos)
+{
+   uint8_t n;
+
+   switch (ty) {
+   case TYPE_U8:
+      n = 0;
+      break;
+   case TYPE_S8:
+      n = 1;
+      break;
+   case TYPE_U16:
+      n = 2;
+      break;
+   case TYPE_S16:
+      n = 3;
+      break;
+   case TYPE_F32:
+   case TYPE_U32:
+   case TYPE_S32:
+      n = 4;
+      break;
+   case TYPE_F64:
+   case TYPE_U64:
+   case TYPE_S64:
+      n = 5;
+      break;
+   case TYPE_B128:
+      n = 6;
+      break;
+   default:
+      n = 0;
+      assert(!"invalid ld/st type");
+      break;
+   }
+   code[pos / 32] |= n << (pos % 32);
+}
+
+void
+CodeEmitterGK110::emitCachingMode(CacheMode c, const int pos)
+{
+   uint8_t n;
+
+   switch (c) {
+   case CACHE_CA:
+// case CACHE_WB:
+      n = 0;
+      break;
+   case CACHE_CG:
+      n = 1;
+      break;
+   case CACHE_CS:
+      n = 2;
+      break;
+   case CACHE_CV:
+// case CACHE_WT:
+      n = 3;
+      break;
+   default:
+      n = 0;
+      assert(!"invalid caching mode");
+      break;
+   }
+   code[pos / 32] |= n << (pos % 32);
+}
+
+void
+CodeEmitterGK110::emitSTORE(const Instruction *i)
+{
+   int32_t offset = SDATA(i->src(0)).offset;
+
+   switch (i->src(0).getFile()) {
+   case FILE_MEMORY_GLOBAL: code[1] = 0xe0000000; code[0] = 0x00000000; break;
+   case FILE_MEMORY_LOCAL:  code[1] = 0x7a800000; code[0] = 0x00000002; break;
+   case FILE_MEMORY_SHARED: code[1] = 0x7ac00000; code[0] = 0x00000002; break;
+   default:
+      assert(!"invalid memory file");
+      break;
+   }
+
+   if (i->src(0).getFile() != FILE_MEMORY_GLOBAL)
+      offset &= 0xffffff;
+
+   if (code[0] & 0x2) {
+      emitLoadStoreType(i->dType, 0x33);
+      if (i->src(0).getFile() == FILE_MEMORY_LOCAL)
+         emitCachingMode(i->cache, 0x2f);
+   } else {
+      emitLoadStoreType(i->dType, 0x38);
+      emitCachingMode(i->cache, 0x3b);
+   }
+   code[0] |= offset << 23;
+   code[1] |= offset >> 9;
+
+   emitPredicate(i);
+
+   srcId(i->src(1), 2);
+   srcId(i->src(0).getIndirect(0), 10);
+}
+
+void
+CodeEmitterGK110::emitLOAD(const Instruction *i)
+{
+   int32_t offset = SDATA(i->src(0)).offset;
+
+   switch (i->src(0).getFile()) {
+   case FILE_MEMORY_GLOBAL: code[1] = 0xc0000000; code[0] = 0x00000000; break;
+   case FILE_MEMORY_LOCAL:  code[1] = 0x7a000000; code[0] = 0x00000002; break;
+   case FILE_MEMORY_SHARED: code[1] = 0x7ac00000; code[0] = 0x00000002; break;
+   case FILE_MEMORY_CONST:
+      if (!i->src(0).isIndirect(0) && typeSizeof(i->dType) == 4) {
+         emitMOV(i);
+         return;
+      }
+      offset &= 0xffff;
+      code[0] = 0x00000002;
+      code[1] = 0x7c800000 | (i->src(0).get()->reg.fileIndex << 7);
+      break;
+   default:
+      assert(!"invalid memory file");
+      break;
+   }
+
+   if (code[0] & 0x2) {
+      offset &= 0xffffff;
+      emitLoadStoreType(i->dType, 0x33);
+      if (i->src(0).getFile() == FILE_MEMORY_LOCAL)
+         emitCachingMode(i->cache, 0x2f);
+   } else {
+      emitLoadStoreType(i->dType, 0x38);
+      emitCachingMode(i->cache, 0x3b);
+   }
+   code[0] |= offset << 23;
+   code[1] |= offset >> 9;
+
+   emitPredicate(i);
+
+   defId(i->def(0), 2);
+   srcId(i->src(0).getIndirect(0), 10);
+}
+
+uint8_t
+CodeEmitterGK110::getSRegEncoding(const ValueRef& ref)
+{
+   switch (SDATA(ref).sv.sv) {
+   case SV_LANEID:        return 0x00;
+   case SV_PHYSID:        return 0x03;
+   case SV_VERTEX_COUNT:  return 0x10;
+   case SV_INVOCATION_ID: return 0x11;
+   case SV_YDIR:          return 0x12;
+   case SV_TID:           return 0x21 + SDATA(ref).sv.index;
+   case SV_CTAID:         return 0x25 + SDATA(ref).sv.index;
+   case SV_NTID:          return 0x29 + SDATA(ref).sv.index;
+   case SV_GRIDID:        return 0x2c;
+   case SV_NCTAID:        return 0x2d + SDATA(ref).sv.index;
+   case SV_LBASE:         return 0x34;
+   case SV_SBASE:         return 0x30;
+   case SV_CLOCK:         return 0x50 + SDATA(ref).sv.index;
+   default:
+      assert(!"no sreg for system value");
+      return 0;
+   }
+}
+
+void
+CodeEmitterGK110::emitMOV(const Instruction *i)
+{
+   if (i->src(0).getFile() == FILE_SYSTEM_VALUE) {
+      code[0] = 0x00000002 | (getSRegEncoding(i->src(0)) << 23);
+      code[1] = 0x86400000;
+      emitPredicate(i);
+      defId(i->def(0), 2);
+   } else
+   if (i->src(0).getFile() == FILE_IMMEDIATE) {
+      code[0] = 0x00000002 | (i->lanes << 14);
+      code[1] = 0x74000000;
+      emitPredicate(i);
+      defId(i->def(0), 2);
+      setImmediate32(i, 0, Modifier(0));
+   } else
+   if (i->src(0).getFile() == FILE_PREDICATE) {
+      // TODO
+   } else {
+      emitForm_C(i, 0x24c, 2);
+      code[1] |= i->lanes << 10;
+   }
+}
+
+bool
+CodeEmitterGK110::emitInstruction(Instruction *insn)
+{
+   const unsigned int size = (writeIssueDelays && !(codeSize & 0x3f)) ? 16 : 8;
+
+   if (insn->encSize != 8) {
+      ERROR("skipping unencodable instruction: ");
+      insn->print();
+      return false;
+   } else
+   if (codeSize + size > codeSizeLimit) {
+      ERROR("code emitter output buffer too small\n");
+      return false;
+   }
+
+   if (writeIssueDelays) {
+      int id = (codeSize & 0x3f) / 8 - 1;
+      if (id < 0) {
+         id += 1;
+         code[0] = 0x00000000; // cf issue delay "instruction"
+         code[1] = 0x08000000;
+         code += 2;
+         codeSize += 8;
+      }
+      uint32_t *data = code - (id * 2 + 2);
+
+      switch (id) {
+      case 0: data[0] |= insn->sched << 2; break;
+      case 1: data[0] |= insn->sched << 10; break;
+      case 2: data[0] |= insn->sched << 18; break;
+      case 3: data[0] |= insn->sched << 26; data[1] |= insn->sched >> 6; break;
+      case 4: data[1] |= insn->sched << 2;
+      case 5: data[1] |= insn->sched << 10; break;
+      case 6: data[1] |= insn->sched << 18; break;
+      default:
+         assert(0);
+         break;
+      }
+   }
+
+   // assert that instructions with multiple defs don't corrupt registers
+   for (int d = 0; insn->defExists(d); ++d)
+      assert(insn->asTex() || insn->def(d).rep()->reg.data.id >= 0);
+
+   switch (insn->op) {
+   case OP_MOV:
+   case OP_RDSV:
+      emitMOV(insn);
+      break;
+   case OP_NOP:
+      break;
+   case OP_LOAD:
+      emitLOAD(insn);
+      break;
+   case OP_STORE:
+      emitSTORE(insn);
+      break;
+   case OP_LINTERP:
+   case OP_PINTERP:
+      emitINTERP(insn);
+      break;
+   case OP_VFETCH:
+      emitVFETCH(insn);
+      break;
+   case OP_EXPORT:
+      emitEXPORT(insn);
+      break;
+   case OP_PFETCH:
+      emitPFETCH(insn);
+      break;
+   case OP_EMIT:
+   case OP_RESTART:
+      emitOUT(insn);
+      break;
+   case OP_ADD:
+   case OP_SUB:
+      if (isFloatType(insn->dType))
+         emitFADD(insn);
+      else
+         emitUADD(insn);
+      break;
+   case OP_MUL:
+      if (isFloatType(insn->dType))
+         emitFMUL(insn);
+      else
+         emitIMUL(insn);
+      break;
+   case OP_MAD:
+   case OP_FMA:
+      if (isFloatType(insn->dType))
+         emitFMAD(insn);
+      else
+         emitIMAD(insn);
+      break;
+   case OP_SAD:
+      emitISAD(insn);
+      break;
+   case OP_NOT:
+      emitNOT(insn);
+      break;
+   case OP_AND:
+      emitLogicOp(insn, 0);
+      break;
+   case OP_OR:
+      emitLogicOp(insn, 1);
+      break;
+   case OP_XOR:
+      emitLogicOp(insn, 2);
+      break;
+   case OP_SHL:
+   case OP_SHR:
+      emitShift(insn);
+      break;
+   case OP_SET:
+   case OP_SET_AND:
+   case OP_SET_OR:
+   case OP_SET_XOR:
+      emitSET(insn->asCmp());
+      break;
+   case OP_SELP:
+      emitSELP(insn);
+      break;
+   case OP_SLCT:
+      emitSLCT(insn->asCmp());
+      break;
+   case OP_MIN:
+   case OP_MAX:
+      emitMINMAX(insn);
+      break;
+   case OP_ABS:
+   case OP_NEG:
+   case OP_CEIL:
+   case OP_FLOOR:
+   case OP_TRUNC:
+   case OP_CVT:
+   case OP_SAT:
+      emitCVT(insn);
+      break;
+   case OP_RSQ:
+      emitSFnOp(insn, 5);
+      break;
+   case OP_RCP:
+      emitSFnOp(insn, 4);
+      break;
+   case OP_LG2:
+      emitSFnOp(insn, 3);
+      break;
+   case OP_EX2:
+      emitSFnOp(insn, 2);
+      break;
+   case OP_SIN:
+      emitSFnOp(insn, 1);
+      break;
+   case OP_COS:
+      emitSFnOp(insn, 0);
+      break;
+   case OP_PRESIN:
+   case OP_PREEX2:
+      emitPreOp(insn);
+      break;
+   case OP_TEX:
+   case OP_TXB:
+   case OP_TXL:
+   case OP_TXD:
+   case OP_TXF:
+      emitTEX(insn->asTex());
+      break;
+   case OP_TXQ:
+      emitTXQ(insn->asTex());
+      break;
+   case OP_TEXBAR:
+      emitTEXBAR(insn);
+      break;
+   case OP_BRA:
+   case OP_CALL:
+   case OP_PRERET:
+   case OP_RET:
+   case OP_DISCARD:
+   case OP_EXIT:
+   case OP_PRECONT:
+   case OP_CONT:
+   case OP_PREBREAK:
+   case OP_BREAK:
+   case OP_JOINAT:
+   case OP_BRKPT:
+   case OP_QUADON:
+   case OP_QUADPOP:
+      emitFlow(insn);
+      break;
+   case OP_QUADOP:
+      emitQUADOP(insn, insn->subOp, insn->lanes);
+      break;
+   case OP_DFDX:
+      emitQUADOP(insn, insn->src(0).mod.neg() ? 0x66 : 0x99, 0x4);
+      break;
+   case OP_DFDY:
+      emitQUADOP(insn, insn->src(0).mod.neg() ? 0x5a : 0xa5, 0x5);
+      break;
+   case OP_POPCNT:
+      emitPOPC(insn);
+      break;
+   case OP_JOIN:
+      emitNOP(insn);
+      insn->join = 1;
+      break;
+   case OP_PHI:
+   case OP_UNION:
+   case OP_CONSTRAINT:
+      ERROR("operation should have been eliminated");
+      return false;
+   case OP_EXP:
+   case OP_LOG:
+   case OP_SQRT:
+   case OP_POW:
+      ERROR("operation should have been lowered\n");
+      return false;
+   default:
+      ERROR("unknow op\n");
+      return false;
+   }
+
+   if (insn->join)
+      code[0] |= 1 << 22;
+
+   code += 2;
+   codeSize += 8;
+   return true;
+}
+
+uint32_t
+CodeEmitterGK110::getMinEncodingSize(const Instruction *i) const
+{
+   // No more short instruction encodings.
+   return 8;
+}
+
+void
+CodeEmitterGK110::prepareEmission(Function *func)
+{
+   const Target *targ = func->getProgram()->getTarget();
+
+   CodeEmitter::prepareEmission(func);
+
+   if (targ->hasSWSched)
+      calculateSchedDataNVC0(targ, func);
+}
+
+CodeEmitterGK110::CodeEmitterGK110(const TargetNVC0 *target)
+   : CodeEmitter(target),
+     writeIssueDelays(target->hasSWSched)
+{
+   code = NULL;
+   codeSize = codeSizeLimit = 0;
+   relocInfo = NULL;
+}
+
+CodeEmitter *
+TargetNVC0::createCodeEmitterGK110(Program::Type type)
+{
+   CodeEmitterGK110 *emit = new CodeEmitterGK110(this);
+   emit->setProgramType(type);
+   return emit;
+}
+
+} // namespace nv50_ir
index 57d5d723c6a1886f4af4a33e2d75f589e23f3b99..cd04f4cfb97d24f3196ad2bb79378646a9f454c4 100644 (file)
@@ -2298,6 +2298,13 @@ SchedDataCalculator::recordRd(const Value *v, const int ready)
    }
 }
 
+bool
+calculateSchedDataNVC0(const Target *targ, Function *func)
+{
+   SchedDataCalculator sched(targ);
+   return sched.run(func, true, true);
+}
+
 void
 CodeEmitterNVC0::prepareEmission(Function *func)
 {
@@ -2305,10 +2312,8 @@ CodeEmitterNVC0::prepareEmission(Function *func)
 
    CodeEmitter::prepareEmission(func);
 
-   if (targ->hasSWSched) {
-      SchedDataCalculator sched(targ);
-      sched.run(func, true, true);
-   }
+   if (targ->hasSWSched)
+      calculateSchedDataNVC0(targ, func);
 }
 
 CodeEmitterNVC0::CodeEmitterNVC0(const TargetNVC0 *target)
@@ -2321,11 +2326,19 @@ CodeEmitterNVC0::CodeEmitterNVC0(const TargetNVC0 *target)
 }
 
 CodeEmitter *
-TargetNVC0::getCodeEmitter(Program::Type type)
+TargetNVC0::createCodeEmitterNVC0(Program::Type type)
 {
    CodeEmitterNVC0 *emit = new CodeEmitterNVC0(this);
    emit->setProgramType(type);
    return emit;
 }
 
+CodeEmitter *
+TargetNVC0::getCodeEmitter(Program::Type type)
+{
+   if (chipset >= NVISA_GK110_CHIPSET)
+      return createCodeEmitterGK110(type);
+   return createCodeEmitterNVC0(type);
+}
+
 } // namespace nv50_ir
index efb51249115474229f59ebad456a7f7fee3ca585..749ace5d81cd7d0d64327b14bda5c0645815eadd 100644 (file)
@@ -157,7 +157,7 @@ private:
    const Instruction *recurseDef(const Instruction *);
 
 private:
-   LValue *r63;
+   LValue *rZero;
    const bool needTexBar;
 };
 
@@ -467,8 +467,8 @@ NVC0LegalizePostRA::visit(Function *fn)
    if (needTexBar)
       insertTextureBarriers(fn);
 
-   r63 = new_LValue(fn, FILE_GPR);
-   r63->reg.data.id = 63;
+   rZero = new_LValue(fn, FILE_GPR);
+   rZero->reg.data.id = prog->getTarget()->getFileSize(FILE_GPR);
    return true;
 }
 
@@ -478,7 +478,7 @@ NVC0LegalizePostRA::replaceZero(Instruction *i)
    for (int s = 0; i->srcExists(s); ++s) {
       ImmediateValue *imm = i->getSrc(s)->asImm();
       if (imm && imm->reg.data.u64 == 0)
-         i->setSrc(s, r63);
+         i->setSrc(s, rZero);
    }
 }
 
@@ -556,7 +556,7 @@ NVC0LegalizePostRA::visit(BasicBlock *bb)
          if (!i->getDef(0)->refCount())
             i->setDef(0, NULL);
          if (i->src(0).getFile() == FILE_IMMEDIATE)
-            i->setSrc(0, r63); // initial value must be 0
+            i->setSrc(0, rZero); // initial value must be 0
       } else
       if (i->isNop()) {
          bb->remove(i);
index 0674f12704d7fff73237e31b5c827e5409c8c637..e3db4b2318d9b1dc46302e83d0fc3bba3c0c4aa2 100644 (file)
@@ -334,7 +334,7 @@ TargetNVC0::getFileSize(DataFile file) const
 {
    switch (file) {
    case FILE_NULL:          return 0;
-   case FILE_GPR:           return 63;
+   case FILE_GPR:           return (chipset >= NVISA_GK110_CHIPSET) ? 255 : 63;
    case FILE_PREDICATE:     return 7;
    case FILE_FLAGS:         return 1;
    case FILE_ADDRESS:       return 0;
@@ -392,7 +392,7 @@ TargetNVC0::insnCanLoad(const Instruction *i, int s,
 {
    DataFile sf = ld->src(0).getFile();
 
-   // immediate 0 can be represented by GPR $r63
+   // immediate 0 can be represented by GPR $r63/$r255
    if (sf == FILE_IMMEDIATE && ld->getSrc(0)->reg.data.u64 == 0)
       return (!i->asTex() && i->op != OP_EXPORT && i->op != OP_STORE);
 
index d859388dfdf83a00a6ddc0e741f173cf29b2e642..92a9a2a3c6afa765febc6962e4e0a20f0c664742 100644 (file)
@@ -38,6 +38,9 @@ public:
 
    virtual CodeEmitter *getCodeEmitter(Program::Type);
 
+   CodeEmitter *createCodeEmitterNVC0(Program::Type);
+   CodeEmitter *createCodeEmitterGK110(Program::Type);
+
    virtual bool runLegalizePass(Program *, CGStage stage) const;
 
    virtual void getBuiltinCode(const uint32_t **code, uint32_t *size) const;
@@ -64,7 +67,8 @@ public:
 
 private:
    void initOpInfo();
-
 };
 
+bool calculateSchedDataNVC0(const Target *, Function *);
+
 } // namespace nv50_ir