blob: f713e6391c6325ed00a89148541607186da0409e [file] [log] [blame]
/*
* 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 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.h"
#include "nv50_ir_build_util.h"
namespace nv50_ir {
BuildUtil::BuildUtil()
{
init(NULL);
}
BuildUtil::BuildUtil(Program *prog)
{
init(prog);
}
void
BuildUtil::init(Program *prog)
{
this->prog = prog;
func = NULL;
bb = NULL;
pos = NULL;
memset(imms, 0, sizeof(imms));
immCount = 0;
}
void
BuildUtil::addImmediate(ImmediateValue *imm)
{
if (immCount > (NV50_IR_BUILD_IMM_HT_SIZE * 3) / 4)
return;
unsigned int pos = u32Hash(imm->reg.data.u32);
while (imms[pos])
pos = (pos + 1) % NV50_IR_BUILD_IMM_HT_SIZE;
imms[pos] = imm;
immCount++;
}
Instruction *
BuildUtil::mkOp1(operation op, DataType ty, Value *dst, Value *src)
{
Instruction *insn = new_Instruction(func, op, ty);
insn->setDef(0, dst);
insn->setSrc(0, src);
insert(insn);
return insn;
}
Instruction *
BuildUtil::mkOp2(operation op, DataType ty, Value *dst,
Value *src0, Value *src1)
{
Instruction *insn = new_Instruction(func, op, ty);
insn->setDef(0, dst);
insn->setSrc(0, src0);
insn->setSrc(1, src1);
insert(insn);
return insn;
}
Instruction *
BuildUtil::mkOp3(operation op, DataType ty, Value *dst,
Value *src0, Value *src1, Value *src2)
{
Instruction *insn = new_Instruction(func, op, ty);
insn->setDef(0, dst);
insn->setSrc(0, src0);
insn->setSrc(1, src1);
insn->setSrc(2, src2);
insert(insn);
return insn;
}
LValue *
BuildUtil::mkLoad(DataType ty, Symbol *mem, Value *ptr)
{
Instruction *insn = new_Instruction(func, OP_LOAD, ty);
LValue *def = getScratch();
insn->setDef(0, def);
insn->setSrc(0, mem);
if (ptr)
insn->setIndirect(0, 0, ptr);
insert(insn);
return def;
}
Instruction *
BuildUtil::mkStore(operation op, DataType ty, Symbol *mem, Value *ptr,
Value *stVal)
{
Instruction *insn = new_Instruction(func, op, ty);
insn->setSrc(0, mem);
insn->setSrc(1, stVal);
if (ptr)
insn->setIndirect(0, 0, ptr);
insert(insn);
return insn;
}
Instruction *
BuildUtil::mkFetch(Value *dst, DataType ty, DataFile file, int32_t offset,
Value *attrRel, Value *primRel)
{
Symbol *sym = mkSymbol(file, 0, ty, offset);
Instruction *insn = mkOp1(OP_VFETCH, ty, dst, sym);
insn->setIndirect(0, 0, attrRel);
insn->setIndirect(0, 1, primRel);
// already inserted
return insn;
}
Instruction *
BuildUtil::mkInterp(unsigned mode, Value *dst, int32_t offset, Value *rel)
{
operation op = OP_LINTERP;
DataType ty = TYPE_F32;
if ((mode & NV50_IR_INTERP_MODE_MASK) == NV50_IR_INTERP_FLAT)
ty = TYPE_U32;
else
if ((mode & NV50_IR_INTERP_MODE_MASK) == NV50_IR_INTERP_PERSPECTIVE)
op = OP_PINTERP;
Symbol *sym = mkSymbol(FILE_SHADER_INPUT, 0, ty, offset);
Instruction *insn = mkOp1(op, ty, dst, sym);
insn->setIndirect(0, 0, rel);
return insn;
}
Instruction *
BuildUtil::mkMov(Value *dst, Value *src, DataType ty)
{
Instruction *insn = new_Instruction(func, OP_MOV, ty);
insn->setDef(0, dst);
insn->setSrc(0, src);
insert(insn);
return insn;
}
Instruction *
BuildUtil::mkMovToReg(int id, Value *src)
{
Instruction *insn = new_Instruction(func, OP_MOV, typeOfSize(src->reg.size));
insn->setDef(0, new_LValue(func, FILE_GPR));
insn->getDef(0)->reg.data.id = id;
insn->setSrc(0, src);
insert(insn);
return insn;
}
Instruction *
BuildUtil::mkMovFromReg(Value *dst, int id)
{
Instruction *insn = new_Instruction(func, OP_MOV, typeOfSize(dst->reg.size));
insn->setDef(0, dst);
insn->setSrc(0, new_LValue(func, FILE_GPR));
insn->getSrc(0)->reg.data.id = id;
insert(insn);
return insn;
}
Instruction *
BuildUtil::mkCvt(operation op,
DataType dstTy, Value *dst, DataType srcTy, Value *src)
{
Instruction *insn = new_Instruction(func, op, dstTy);
insn->setType(dstTy, srcTy);
insn->setDef(0, dst);
insn->setSrc(0, src);
insert(insn);
return insn;
}
CmpInstruction *
BuildUtil::mkCmp(operation op, CondCode cc, DataType ty, Value *dst,
Value *src0, Value *src1, Value *src2)
{
CmpInstruction *insn = new_CmpInstruction(func, op);
insn->setType((dst->reg.file == FILE_PREDICATE ||
dst->reg.file == FILE_FLAGS) ? TYPE_U8 : ty, ty);
insn->setCondition(cc);
insn->setDef(0, dst);
insn->setSrc(0, src0);
insn->setSrc(1, src1);
if (src2)
insn->setSrc(2, src2);
if (dst->reg.file == FILE_FLAGS)
insn->flagsDef = 0;
insert(insn);
return insn;
}
Instruction *
BuildUtil::mkTex(operation op, TexTarget targ, uint8_t tic, uint8_t tsc,
Value **def, Value **src)
{
TexInstruction *tex = new_TexInstruction(func, op);
for (int d = 0; d < 4 && def[d]; ++d)
tex->setDef(d, def[d]);
for (int s = 0; s < 4 && src[s]; ++s)
tex->setSrc(s, src[s]);
tex->setTexture(targ, tic, tsc);
return tex;
}
Instruction *
BuildUtil::mkQuadop(uint8_t q, Value *def, uint8_t l, Value *src0, Value *src1)
{
Instruction *quadop = mkOp2(OP_QUADOP, TYPE_F32, def, src0, src1);
quadop->subOp = q;
quadop->lanes = l;
return quadop;
}
Instruction *
BuildUtil::mkSelect(Value *pred, Value *dst, Value *trSrc, Value *flSrc)
{
LValue *def0 = getSSA();
LValue *def1 = getSSA();
mkMov(def0, trSrc)->setPredicate(CC_P, pred);
mkMov(def1, flSrc)->setPredicate(CC_NOT_P, pred);
return mkOp2(OP_UNION, typeOfSize(dst->reg.size), dst, def0, def1);
}
Instruction *
BuildUtil::mkSplit(Value *h[2], uint8_t halfSize, Value *val)
{
Instruction *insn = NULL;
const DataType fTy = typeOfSize(halfSize * 2);
if (val->reg.file == FILE_IMMEDIATE)
val = mkMov(getSSA(halfSize * 2), val, fTy)->getDef(0);
if (isMemoryFile(val->reg.file)) {
h[0] = cloneShallow(getFunction(), val);
h[1] = cloneShallow(getFunction(), val);
h[0]->reg.size = halfSize;
h[1]->reg.size = halfSize;
h[1]->reg.data.offset += halfSize;
} else {
h[0] = getSSA(halfSize, val->reg.file);
h[1] = getSSA(halfSize, val->reg.file);
insn = mkOp1(OP_SPLIT, fTy, h[0], val);
insn->setDef(1, h[1]);
}
return insn;
}
FlowInstruction *
BuildUtil::mkFlow(operation op, void *targ, CondCode cc, Value *pred)
{
FlowInstruction *insn = new_FlowInstruction(func, op, targ);
if (pred)
insn->setPredicate(cc, pred);
insert(insn);
return insn;
}
void
BuildUtil::mkClobber(DataFile f, uint32_t rMask, int unit)
{
static const uint16_t baseSize2[16] =
{
0x0000, 0x0010, 0x0011, 0x0020, 0x0012, 0x1210, 0x1211, 0x1220,
0x0013, 0x1310, 0x1311, 0x1320, 0x0022, 0x2210, 0x2211, 0x0040,
};
int base = 0;
for (; rMask; rMask >>= 4, base += 4) {
const uint32_t mask = rMask & 0xf;
if (!mask)
continue;
int base1 = (baseSize2[mask] >> 0) & 0xf;
int size1 = (baseSize2[mask] >> 4) & 0xf;
int base2 = (baseSize2[mask] >> 8) & 0xf;
int size2 = (baseSize2[mask] >> 12) & 0xf;
Instruction *insn = mkOp(OP_NOP, TYPE_NONE, NULL);
if (1) { // size1 can't be 0
LValue *reg = new_LValue(func, f);
reg->reg.size = size1 << unit;
reg->reg.data.id = base + base1;
insn->setDef(0, reg);
}
if (size2) {
LValue *reg = new_LValue(func, f);
reg->reg.size = size2 << unit;
reg->reg.data.id = base + base2;
insn->setDef(1, reg);
}
}
}
ImmediateValue *
BuildUtil::mkImm(uint32_t u)
{
unsigned int pos = u32Hash(u);
while (imms[pos] && imms[pos]->reg.data.u32 != u)
pos = (pos + 1) % NV50_IR_BUILD_IMM_HT_SIZE;
ImmediateValue *imm = imms[pos];
if (!imm) {
imm = new_ImmediateValue(prog, u);
addImmediate(imm);
}
return imm;
}
ImmediateValue *
BuildUtil::mkImm(uint64_t u)
{
ImmediateValue *imm = new_ImmediateValue(prog, (uint32_t)0);
imm->reg.size = 8;
imm->reg.type = TYPE_U64;
imm->reg.data.u64 = u;
return imm;
}
ImmediateValue *
BuildUtil::mkImm(float f)
{
union {
float f32;
uint32_t u32;
} u;
u.f32 = f;
return mkImm(u.u32);
}
Value *
BuildUtil::loadImm(Value *dst, float f)
{
return mkOp1v(OP_MOV, TYPE_F32, dst ? dst : getScratch(), mkImm(f));
}
Value *
BuildUtil::loadImm(Value *dst, uint32_t u)
{
return mkOp1v(OP_MOV, TYPE_U32, dst ? dst : getScratch(), mkImm(u));
}
Value *
BuildUtil::loadImm(Value *dst, uint64_t u)
{
return mkOp1v(OP_MOV, TYPE_U64, dst ? dst : getScratch(8), mkImm(u));
}
Symbol *
BuildUtil::mkSymbol(DataFile file, int8_t fileIndex, DataType ty,
uint32_t baseAddr)
{
Symbol *sym = new_Symbol(prog, file, fileIndex);
sym->setOffset(baseAddr);
sym->reg.type = ty;
sym->reg.size = typeSizeof(ty);
return sym;
}
Symbol *
BuildUtil::mkSysVal(SVSemantic svName, uint32_t svIndex)
{
Symbol *sym = new_Symbol(prog, FILE_SYSTEM_VALUE, 0);
assert(svIndex < 4 ||
(svName == SV_CLIP_DISTANCE || svName == SV_TESS_FACTOR));
switch (svName) {
case SV_POSITION:
case SV_FACE:
case SV_YDIR:
case SV_POINT_SIZE:
case SV_POINT_COORD:
case SV_CLIP_DISTANCE:
case SV_TESS_FACTOR:
sym->reg.type = TYPE_F32;
break;
default:
sym->reg.type = TYPE_U32;
break;
}
sym->reg.size = typeSizeof(sym->reg.type);
sym->reg.data.sv.sv = svName;
sym->reg.data.sv.index = svIndex;
return sym;
}
void
BuildUtil::DataArray::setup(unsigned array, unsigned arrayIdx,
uint32_t base, int len, int vecDim, int eltSize,
DataFile file, int8_t fileIdx)
{
this->array = array;
this->arrayIdx = arrayIdx;
this->baseAddr = base;
this->arrayLen = len;
this->vecDim = vecDim;
this->eltSize = eltSize;
this->file = file;
this->regOnly = !isMemoryFile(file);
if (!regOnly) {
baseSym = new_Symbol(up->getProgram(), file, fileIdx);
baseSym->setOffset(baseAddr);
baseSym->reg.size = eltSize;
} else {
baseSym = NULL;
}
}
Value *
BuildUtil::DataArray::acquire(ValueMap &m, int i, int c)
{
if (regOnly) {
Value *v = lookup(m, i, c);
if (!v)
v = insert(m, i, c, new_LValue(up->getFunction(), file));
return v;
} else {
return up->getScratch();
}
}
Value *
BuildUtil::DataArray::load(ValueMap &m, int i, int c, Value *ptr)
{
if (regOnly) {
Value *v = lookup(m, i, c);
if (!v)
v = insert(m, i, c, new_LValue(up->getFunction(), file));
return v;
} else {
Value *sym = lookup(m, i, c);
if (!sym)
sym = insert(m, i, c, mkSymbol(i, c));
return up->mkLoad(typeOfSize(eltSize), static_cast<Symbol *>(sym), ptr);
}
}
void
BuildUtil::DataArray::store(ValueMap &m, int i, int c, Value *ptr, Value *value)
{
if (regOnly) {
assert(!ptr);
if (!lookup(m, i, c))
insert(m, i, c, value);
assert(lookup(m, i, c) == value);
} else {
Value *sym = lookup(m, i, c);
if (!sym)
sym = insert(m, i, c, mkSymbol(i, c));
const DataType stTy = typeOfSize(value->reg.size);
up->mkStore(OP_STORE, stTy, static_cast<Symbol *>(sym), ptr, value);
}
}
Symbol *
BuildUtil::DataArray::mkSymbol(int i, int c)
{
const unsigned int idx = i * vecDim + c;
Symbol *sym = new_Symbol(up->getProgram(), file, 0);
assert(baseSym || (idx < arrayLen && c < vecDim));
sym->reg.size = eltSize;
sym->reg.type = typeOfSize(eltSize);
sym->setAddress(baseSym, baseAddr + idx * eltSize);
return sym;
}
} // namespace nv50_ir