0,0 → 1,615 |
/* |
* 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. |
*/ |
|
#include "codegen/nv50_ir.h" |
#include "codegen/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; |
} |
|
Instruction * |
BuildUtil::mkLoad(DataType ty, Value *dst, Symbol *mem, Value *ptr) |
{ |
Instruction *insn = new_Instruction(func, OP_LOAD, ty); |
|
insn->setDef(0, dst); |
insn->setSrc(0, mem); |
if (ptr) |
insn->setIndirect(0, 0, ptr); |
|
insert(insn); |
return insn; |
} |
|
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); |
insn->setInterpolate(mode); |
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 dstTy, Value *dst, |
DataType srcTy, 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 : dstTy, srcTy); |
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; |
} |
|
TexInstruction * |
BuildUtil::mkTex(operation op, TexTarget targ, |
uint16_t tic, uint16_t tsc, |
const std::vector<Value *> &def, |
const std::vector<Value *> &src) |
{ |
TexInstruction *tex = new_TexInstruction(func, op); |
|
for (size_t d = 0; d < def.size() && def[d]; ++d) |
tex->setDef(d, def[d]); |
for (size_t s = 0; s < src.size() && src[s]; ++s) |
tex->setSrc(s, src[s]); |
|
tex->setTexture(targ, tic, tsc); |
|
insert(tex); |
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->mkLoadv(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; |
} |
|
|
Instruction * |
BuildUtil::split64BitOpPostRA(Function *fn, Instruction *i, |
Value *zero, |
Value *carry) |
{ |
DataType hTy; |
int srcNr; |
|
switch (i->dType) { |
case TYPE_U64: hTy = TYPE_U32; break; |
case TYPE_S64: hTy = TYPE_S32; break; |
default: |
return NULL; |
} |
|
switch (i->op) { |
case OP_MOV: srcNr = 1; break; |
case OP_ADD: |
case OP_SUB: |
if (!carry) |
return NULL; |
srcNr = 2; |
break; |
default: |
// TODO when needed |
return NULL; |
} |
|
i->setType(hTy); |
i->setDef(0, cloneShallow(fn, i->getDef(0))); |
i->getDef(0)->reg.size = 4; |
Instruction *lo = i; |
Instruction *hi = cloneForward(fn, i); |
lo->bb->insertAfter(lo, hi); |
|
hi->getDef(0)->reg.data.id++; |
|
for (int s = 0; s < srcNr; ++s) { |
if (lo->getSrc(s)->reg.size < 8) { |
hi->setSrc(s, zero); |
} else { |
if (lo->getSrc(s)->refCount() > 1) |
lo->setSrc(s, cloneShallow(fn, lo->getSrc(s))); |
lo->getSrc(s)->reg.size /= 2; |
hi->setSrc(s, cloneShallow(fn, lo->getSrc(s))); |
|
switch (hi->src(s).getFile()) { |
case FILE_IMMEDIATE: |
hi->getSrc(s)->reg.data.u64 >>= 32; |
break; |
case FILE_MEMORY_CONST: |
case FILE_MEMORY_SHARED: |
case FILE_SHADER_INPUT: |
hi->getSrc(s)->reg.data.offset += 4; |
break; |
default: |
assert(hi->src(s).getFile() == FILE_GPR); |
hi->getSrc(s)->reg.data.id++; |
break; |
} |
} |
} |
if (srcNr == 2) { |
lo->setDef(1, carry); |
hi->setFlagsSrc(hi->srcCount(), carry); |
} |
return hi; |
} |
|
} // namespace nv50_ir |