text: /*
   * Copyright 2011 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 OR COPYRIGHT HOLDERS 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.
   */
  
  #ifndef __NV50_IR_BUILD_UTIL__
  #define __NV50_IR_BUILD_UTIL__
  
  namespace nv50_ir {
  
  class BuildUtil
  {
  public:
     BuildUtil();
     BuildUtil(Program *);
  
     inline void setProgram(Program *);
     inline Program *getProgram() const { return prog; }
     inline Function *getFunction() const { return func; }
  
     // keeps inserting at head/tail of block
     inline void setPosition(BasicBlock *, bool tail);
     // position advances only if @after is true
     inline void setPosition(Instruction *, bool after);
  
     inline BasicBlock *getBB() { return bb; }
  
     inline void insert(Instruction *);
     inline void remove(Instruction *i) { assert(i->bb == bb); bb->remove(i); }
  
     inline LValue *getScratch(int size = 4, DataFile = FILE_GPR);
     // scratch value for a single assignment:
     inline LValue *getSSA(int size = 4, DataFile = FILE_GPR);
  
     inline Instruction *mkOp(operation, DataType, Value *);
     Instruction *mkOp1(operation, DataType, Value *, Value *);
     Instruction *mkOp2(operation, DataType, Value *, Value *, Value *);
     Instruction *mkOp3(operation, DataType, Value *, Value *, Value *, Value *);
  
     LValue *mkOp1v(operation, DataType, Value *, Value *);
     LValue *mkOp2v(operation, DataType, Value *, Value *, Value *);
     LValue *mkOp3v(operation, DataType, Value *, Value *, Value *, Value *);
  
     Instruction *mkLoad(DataType, Value *dst, Symbol *, Value *ptr);
     Instruction *mkStore(operation, DataType, Symbol *, Value *ptr, Value *val);
  
     LValue *mkLoadv(DataType, Symbol *, Value *ptr);
  
     Instruction *mkMov(Value *, Value *, DataType = TYPE_U32);
     Instruction *mkMovToReg(int id, Value *);
     Instruction *mkMovFromReg(Value *, int id);
  
     Instruction *mkInterp(unsigned mode, Value *, int32_t offset, Value *rel);
     Instruction *mkFetch(Value *, DataType, DataFile, int32_t offset,
                          Value *attrRel, Value *primRel);
  
     Instruction *mkCvt(operation, DataType, Value *, DataType, Value *);
     CmpInstruction *mkCmp(operation, CondCode, DataType,
                           Value *,
                           DataType, Value *, Value *, Value * = NULL);
     TexInstruction *mkTex(operation, TexTarget,
                           uint16_t tic, uint16_t tsc,
                           const std::vector<Value *> &def,
                           const std::vector<Value *> &src);
     Instruction *mkQuadop(uint8_t qop, Value *, uint8_t l, Value *, Value *);
  
     FlowInstruction *mkFlow(operation, void *target, CondCode, Value *pred);
  
     Instruction *mkSelect(Value *pred, Value *dst, Value *trSrc, Value *flSrc);
  
     Instruction *mkSplit(Value *half[2], uint8_t halfSize, Value *);
  
     void mkClobber(DataFile file, uint32_t regMask, int regUnitLog2);
  
     ImmediateValue *mkImm(float);
     ImmediateValue *mkImm(double);
     ImmediateValue *mkImm(uint32_t);
     ImmediateValue *mkImm(uint64_t);
  
     ImmediateValue *mkImm(int i) { return mkImm((uint32_t)i); }
  
     Value *loadImm(Value *dst, float);
     Value *loadImm(Value *dst, double);
     Value *loadImm(Value *dst, uint32_t);
     Value *loadImm(Value *dst, uint64_t);
  
     Value *loadImm(Value *dst, int i) { return loadImm(dst, (uint32_t)i); }
  
     // returns high part of the operation
     static Instruction *split64BitOpPostRA(Function *, Instruction *,
                                            Value *zero, Value *carry);
  
     struct Location
     {
        Location(unsigned array, unsigned arrayIdx, unsigned i, unsigned c)
           : array(array), arrayIdx(arrayIdx), i(i), c(c) { }
        Location(const Location &l)
           : array(l.array), arrayIdx(l.arrayIdx), i(l.i), c(l.c) { }
  
        bool operator==(const Location &l) const
        {
           return
              array == l.array && arrayIdx == l.arrayIdx && i == l.i && c == l.c;
        }
  
        bool operator<(const Location &l) const
        {
           return array != l.array ? array < l.array :
              arrayIdx != l.arrayIdx ? arrayIdx < l.arrayIdx :
              i != l.i ? i < l.i :
              c != l.c ? c < l.c :
              false;
        }
  
        unsigned array, arrayIdx, i, c;
     };
  
     typedef bimap<Location, Value *> ValueMap;
  
     class DataArray
     {
     public:
        DataArray(BuildUtil *bld) : up(bld) { }
  
        void setup(unsigned array, unsigned arrayIdx,
                   uint32_t base, int len, int vecDim, int eltSize,
                   DataFile file, int8_t fileIdx);
  
        inline bool exists(ValueMap&, unsigned int i, unsigned int c);
  
        Value *load(ValueMap&, int i, int c, Value *ptr);
        void store(ValueMap&, int i, int c, Value *ptr, Value *value);
        Value *acquire(ValueMap&, int i, int c);
  
     private:
        inline Value *lookup(ValueMap&, unsigned i, unsigned c);
        inline Value *insert(ValueMap&, unsigned i, unsigned c, Value *v);
  
        Symbol *mkSymbol(int i, int c);
  
     private:
        BuildUtil *up;
        unsigned array, arrayIdx;
  
        uint32_t baseAddr;
        uint32_t arrayLen;
        Symbol *baseSym;
  
        uint8_t vecDim;
        uint8_t eltSize; // in bytes
  
        DataFile file;
        bool regOnly;
     };
  
     Symbol *mkSymbol(DataFile file, int8_t fileIndex,
                      DataType ty, uint32_t baseAddress);
  
     Symbol *mkSysVal(SVSemantic svName, uint32_t svIndex);
  
  private:
     void init(Program *);
     void addImmediate(ImmediateValue *);
     inline unsigned int u32Hash(uint32_t);
  
  protected:
     Program *prog;
     Function *func;
     Instruction *pos;
     BasicBlock *bb;
     bool tail;
  
  #define NV50_IR_BUILD_IMM_HT_SIZE 256
  
     ImmediateValue *imms[NV50_IR_BUILD_IMM_HT_SIZE];
     unsigned int immCount;
  };
  
  unsigned int BuildUtil::u32Hash(uint32_t u)
  {
     return (u % 273) % NV50_IR_BUILD_IMM_HT_SIZE;
  }
  
  void BuildUtil::setProgram(Program *program)
  {
     prog = program;
  }
  
  void
  BuildUtil::setPosition(BasicBlock *block, bool atTail)
  {
     bb = block;
     prog = bb->getProgram();
     func = bb->getFunction();
     pos = NULL;
     tail = atTail;
  }
  
  void
  BuildUtil::setPosition(Instruction *i, bool after)
  {
     bb = i->bb;
     prog = bb->getProgram();
     func = bb->getFunction();
     pos = i;
     tail = after;
     assert(bb);
  }
  
  LValue *
  BuildUtil::getScratch(int size, DataFile f)
  {
     LValue *lval = new_LValue(func, f);
     lval->reg.size = size;
     return lval;
  }
  
  LValue *
  BuildUtil::getSSA(int size, DataFile f)
  {
     LValue *lval = new_LValue(func, f);
     lval->ssa = 1;
     lval->reg.size = size;
     return lval;
  }
  
  void BuildUtil::insert(Instruction *i)
  {
     if (!pos) {
        tail ? bb->insertTail(i) : bb->insertHead(i);
     } else {
        if (tail) {
           bb->insertAfter(pos, i);
           pos = i;
        } else {
           bb->insertBefore(pos, i);
        }
     }
  }
  
  Instruction *
  BuildUtil::mkOp(operation op, DataType ty, Value *dst)
  {
     Instruction *insn = new_Instruction(func, op, ty);
     insn->setDef(0, dst);
     insert(insn);
     if (op == OP_DISCARD || op == OP_EXIT ||
         op == OP_JOIN ||
         op == OP_QUADON || op == OP_QUADPOP ||
         op == OP_EMIT || op == OP_RESTART)
        insn->fixed = 1;
     return insn;
  }
  
  inline LValue *
  BuildUtil::mkOp1v(operation op, DataType ty, Value *dst, Value *src)
  {
     mkOp1(op, ty, dst, src);
     return dst->asLValue();
  }
  
  inline LValue *
  BuildUtil::mkOp2v(operation op, DataType ty, Value *dst,
                    Value *src0, Value *src1)
  {
     mkOp2(op, ty, dst, src0, src1);
     return dst->asLValue();
  }
  
  inline LValue *
  BuildUtil::mkOp3v(operation op, DataType ty, Value *dst,
                    Value *src0, Value *src1, Value *src2)
  {
     mkOp3(op, ty, dst, src0, src1, src2);
     return dst->asLValue();
  }
  
  inline LValue *
  BuildUtil::mkLoadv(DataType ty, Symbol *mem, Value *ptr)
  {
     LValue *dst = getScratch(typeSizeof(ty));
     mkLoad(ty, dst, mem, ptr);
     return dst;
  }
  
  bool
  BuildUtil::DataArray::exists(ValueMap &m, unsigned int i, unsigned int c)
  {
     assert(i < arrayLen && c < vecDim);
     return !regOnly || m.r.count(Location(array, arrayIdx, i, c));
  }
  
  Value *
  BuildUtil::DataArray::lookup(ValueMap &m, unsigned i, unsigned c)
  {
     ValueMap::r_iterator it = m.r.find(Location(array, arrayIdx, i, c));
     return it != m.r.end() ? it->second : NULL;
  }
  
  Value *
  BuildUtil::DataArray::insert(ValueMap &m, unsigned i, unsigned c, Value *v)
  {
     m.insert(Location(array, arrayIdx, i, c), v);
     return v;
  }
  
  } // namespace nv50_ir
  
  #endif // __NV50_IR_BUILD_UTIL_H__
  