blob: b1e4e481621a120da27dacb48f165e01452df57a [file] [log] [blame] [edit]
///////////////////////////////////////////////////////////////////////////////
// //
// DxilCounters.cpp //
// Copyright (C) Microsoft Corporation. All rights reserved. //
// This file is distributed under the University of Illinois Open Source //
// License. See LICENSE.TXT for details. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/DXIL/DxilCounters.h"
#include "dxc/Support/Global.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "dxc/DXIL/DxilInstructions.h"
#include "dxc/DXIL/DxilOperations.h"
using namespace llvm;
using namespace hlsl;
using namespace hlsl::DXIL;
namespace hlsl {
namespace {
struct PointerInfo {
enum class MemType : unsigned {
Unknown = 0,
Global_Static,
Global_TGSM,
Alloca
};
MemType memType : 2;
bool isArray : 1;
PointerInfo() : memType(MemType::Unknown), isArray(false) {}
};
typedef DenseMap<Value *, PointerInfo> PointerInfoMap;
PointerInfo GetPointerInfo(Value *V, PointerInfoMap &ptrInfoMap) {
auto it = ptrInfoMap.find(V);
if (it != ptrInfoMap.end())
return it->second;
Type *Ty = V->getType()->getPointerElementType();
ptrInfoMap[V].isArray = Ty->isArrayTy();
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) {
if (GV->getType()->getPointerAddressSpace() == DXIL::kTGSMAddrSpace)
ptrInfoMap[V].memType = PointerInfo::MemType::Global_TGSM;
else if (!GV->isConstant() &&
GV->getLinkage() ==
GlobalVariable::LinkageTypes::InternalLinkage &&
GV->getType()->getPointerAddressSpace() == DXIL::kDefaultAddrSpace)
ptrInfoMap[V].memType = PointerInfo::MemType::Global_Static;
} else if (isa<AllocaInst>(V)) {
ptrInfoMap[V].memType = PointerInfo::MemType::Alloca;
} else if (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
ptrInfoMap[V] = GetPointerInfo(GEP->getPointerOperand(), ptrInfoMap);
} else if (BitCastOperator *BC = dyn_cast<BitCastOperator>(V)) {
ptrInfoMap[V] = GetPointerInfo(BC->getOperand(0), ptrInfoMap);
} else if (AddrSpaceCastInst *AC = dyn_cast<AddrSpaceCastInst>(V)) {
ptrInfoMap[V] = GetPointerInfo(AC->getOperand(0), ptrInfoMap);
} else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(V)) {
if (CE->getOpcode() == LLVMAddrSpaceCast)
llvm_unreachable("address space cast is illegal in DxilCounters.");
//} else if (PHINode *PN = dyn_cast<PHINode>(V)) {
// for (auto it = PN->value_op_begin(), e = PN->value_op_end(); it != e;
// ++it) {
// PI = GetPointerInfo(*it, ptrInfoMap);
// if (PI.memType != PointerInfo::MemType::Unknown)
// break;
// }
}
return ptrInfoMap[V];
}
struct ValueInfo {
bool isCbuffer : 1;
bool isConstant : 1;
ValueInfo() : isCbuffer(false), isConstant(false) {}
ValueInfo Combine(const ValueInfo &other) const {
ValueInfo R;
R.isCbuffer = isCbuffer && other.isCbuffer;
R.isConstant = isConstant && other.isConstant;
return R;
}
};
/*<py>
def tab_lines(text):
return [' ' + line for line in text.splitlines()]
def gen_count_dxil_op(counter):
return (['bool CountDxilOp_%s(unsigned op) {' % counter] +
tab_lines(
hctdb_instrhelp.get_instrs_pred("op",
hctdb_instrhelp.counter_pred(counter, True))) +
['}'])
def gen_count_llvm_op(counter):
return (['bool CountLlvmOp_%s(unsigned op) {' % counter] +
tab_lines(
hctdb_instrhelp.get_instrs_pred("op",
hctdb_instrhelp.counter_pred(counter, False), 'llvm_id')) +
['}'])
def gen_counter_functions():
lines = ['// Counter functions for Dxil ops:']
for counter in hctdb_instrhelp.get_dxil_op_counters():
lines += gen_count_dxil_op(counter)
lines.append('// Counter functions for llvm ops:')
for counter in hctdb_instrhelp.get_llvm_op_counters():
lines += gen_count_llvm_op(counter)
return lines
</py>*/
// <py::lines('OPCODE-COUNTERS')>gen_counter_functions()</py>
// OPCODE-COUNTERS:BEGIN
// Counter functions for Dxil ops:
bool CountDxilOp_atomic(unsigned op) {
// Instructions: BufferUpdateCounter=70, AtomicBinOp=78,
// AtomicCompareExchange=79
return op == 70 || (78 <= op && op <= 79);
}
bool CountDxilOp_barrier(unsigned op) {
// Instructions: Barrier=80
return op == 80;
}
bool CountDxilOp_floats(unsigned op) {
// Instructions: FAbs=6, Saturate=7, IsNaN=8, IsInf=9, IsFinite=10,
// IsNormal=11, Cos=12, Sin=13, Tan=14, Acos=15, Asin=16, Atan=17, Hcos=18,
// Hsin=19, Htan=20, Exp=21, Frc=22, Log=23, Sqrt=24, Rsqrt=25, Round_ne=26,
// Round_ni=27, Round_pi=28, Round_z=29, FMax=35, FMin=36, Fma=47, Dot2=54,
// Dot3=55, Dot4=56, Dot2AddHalf=162
return (6 <= op && op <= 29) || (35 <= op && op <= 36) || op == 47 ||
(54 <= op && op <= 56) || op == 162;
}
bool CountDxilOp_gs_cut(unsigned op) {
// Instructions: CutStream=98, EmitThenCutStream=99
return (98 <= op && op <= 99);
}
bool CountDxilOp_gs_emit(unsigned op) {
// Instructions: EmitStream=97, EmitThenCutStream=99
return op == 97 || op == 99;
}
bool CountDxilOp_ints(unsigned op) {
// Instructions: IMax=37, IMin=38, IMul=41, IMad=48, Ibfe=51,
// Dot4AddI8Packed=163
return (37 <= op && op <= 38) || op == 41 || op == 48 || op == 51 ||
op == 163;
}
bool CountDxilOp_sig_ld(unsigned op) {
// Instructions: LoadInput=4, LoadOutputControlPoint=103,
// LoadPatchConstant=104
return op == 4 || (103 <= op && op <= 104);
}
bool CountDxilOp_sig_st(unsigned op) {
// Instructions: StoreOutput=5, StorePatchConstant=106, StoreVertexOutput=171,
// StorePrimitiveOutput=172
return op == 5 || op == 106 || (171 <= op && op <= 172);
}
bool CountDxilOp_tex_bias(unsigned op) {
// Instructions: SampleBias=61
return op == 61;
}
bool CountDxilOp_tex_cmp(unsigned op) {
// Instructions: SampleCmp=64, SampleCmpLevelZero=65, TextureGatherCmp=74,
// SampleCmpLevel=224
return (64 <= op && op <= 65) || op == 74 || op == 224;
}
bool CountDxilOp_tex_grad(unsigned op) {
// Instructions: SampleGrad=63
return op == 63;
}
bool CountDxilOp_tex_load(unsigned op) {
// Instructions: TextureLoad=66, BufferLoad=68, RawBufferLoad=139
return op == 66 || op == 68 || op == 139;
}
bool CountDxilOp_tex_norm(unsigned op) {
// Instructions: Sample=60, SampleLevel=62, TextureGather=73,
// TextureGatherRaw=223
return op == 60 || op == 62 || op == 73 || op == 223;
}
bool CountDxilOp_tex_store(unsigned op) {
// Instructions: TextureStore=67, BufferStore=69, RawBufferStore=140,
// WriteSamplerFeedback=174, WriteSamplerFeedbackBias=175,
// WriteSamplerFeedbackLevel=176, WriteSamplerFeedbackGrad=177,
// TextureStoreSample=225
return op == 67 || op == 69 || op == 140 || (174 <= op && op <= 177) ||
op == 225;
}
bool CountDxilOp_uints(unsigned op) {
// Instructions: Bfrev=30, Countbits=31, FirstbitLo=32, FirstbitHi=33,
// FirstbitSHi=34, UMax=39, UMin=40, UMul=42, UDiv=43, UAddc=44, USubb=45,
// UMad=49, Msad=50, Ubfe=52, Bfi=53, Dot4AddU8Packed=164
return (30 <= op && op <= 34) || (39 <= op && op <= 40) ||
(42 <= op && op <= 45) || (49 <= op && op <= 50) ||
(52 <= op && op <= 53) || op == 164;
}
// Counter functions for llvm ops:
bool CountLlvmOp_atomic(unsigned op) {
// Instructions: AtomicCmpXchg=31, AtomicRMW=32
return (31 <= op && op <= 32);
}
bool CountLlvmOp_fence(unsigned op) {
// Instructions: Fence=30
return op == 30;
}
bool CountLlvmOp_floats(unsigned op) {
// Instructions: FAdd=9, FSub=11, FMul=13, FDiv=16, FRem=19, FPToUI=36,
// FPToSI=37, UIToFP=38, SIToFP=39, FPTrunc=40, FPExt=41, FCmp=47
return op == 9 || op == 11 || op == 13 || op == 16 || op == 19 ||
(36 <= op && op <= 41) || op == 47;
}
bool CountLlvmOp_ints(unsigned op) {
// Instructions: Add=8, Sub=10, Mul=12, SDiv=15, SRem=18, AShr=22, Trunc=33,
// SExt=35, ICmp=46
return op == 8 || op == 10 || op == 12 || op == 15 || op == 18 || op == 22 ||
op == 33 || op == 35 || op == 46;
}
bool CountLlvmOp_uints(unsigned op) {
// Instructions: UDiv=14, URem=17, Shl=20, LShr=21, And=23, Or=24, Xor=25,
// ZExt=34
return op == 14 || op == 17 || (20 <= op && op <= 21) ||
(23 <= op && op <= 25) || op == 34;
}
// OPCODE-COUNTERS:END
void CountDxilOp(unsigned op, DxilCounters &counters) {
// clang-format off
// Python lines need to be not formatted.
// <py::lines('COUNT-DXIL-OPS')>['if (CountDxilOp_%s(op)) ++counters.%s;' % (c,c) for c in hctdb_instrhelp.get_dxil_op_counters()]</py>
// clang-format on
// COUNT-DXIL-OPS:BEGIN
if (CountDxilOp_atomic(op))
++counters.atomic;
if (CountDxilOp_barrier(op))
++counters.barrier;
if (CountDxilOp_floats(op))
++counters.floats;
if (CountDxilOp_gs_cut(op))
++counters.gs_cut;
if (CountDxilOp_gs_emit(op))
++counters.gs_emit;
if (CountDxilOp_ints(op))
++counters.ints;
if (CountDxilOp_sig_ld(op))
++counters.sig_ld;
if (CountDxilOp_sig_st(op))
++counters.sig_st;
if (CountDxilOp_tex_bias(op))
++counters.tex_bias;
if (CountDxilOp_tex_cmp(op))
++counters.tex_cmp;
if (CountDxilOp_tex_grad(op))
++counters.tex_grad;
if (CountDxilOp_tex_load(op))
++counters.tex_load;
if (CountDxilOp_tex_norm(op))
++counters.tex_norm;
if (CountDxilOp_tex_store(op))
++counters.tex_store;
if (CountDxilOp_uints(op))
++counters.uints;
// COUNT-DXIL-OPS:END
}
void CountLlvmOp(unsigned op, DxilCounters &counters) {
// clang-format off
// Python lines need to be not formatted.
// <py::lines('COUNT-LLVM-OPS')>['if (CountLlvmOp_%s(op)) ++counters.%s;' % (c,c) for c in hctdb_instrhelp.get_llvm_op_counters()]</py>
// clang-format on
// COUNT-LLVM-OPS:BEGIN
if (CountLlvmOp_atomic(op))
++counters.atomic;
if (CountLlvmOp_fence(op))
++counters.fence;
if (CountLlvmOp_floats(op))
++counters.floats;
if (CountLlvmOp_ints(op))
++counters.ints;
if (CountLlvmOp_uints(op))
++counters.uints;
// COUNT-LLVM-OPS:END
}
} // namespace
void CountInstructions(llvm::Module &M, DxilCounters &counters) {
const DataLayout &DL = M.getDataLayout();
PointerInfoMap ptrInfoMap;
for (auto &GV : M.globals()) {
PointerInfo PI = GetPointerInfo(&GV, ptrInfoMap);
if (PI.isArray) {
// Count number of bytes used in global arrays.
Type *pTy = GV.getType()->getPointerElementType();
uint32_t size = DL.getTypeAllocSize(pTy);
switch (PI.memType) {
case PointerInfo::MemType::Global_Static:
counters.array_static_bytes += size;
break;
case PointerInfo::MemType::Global_TGSM:
counters.array_tgsm_bytes += size;
break;
default:
break;
}
}
}
for (auto &F : M.functions()) {
if (F.isDeclaration())
continue;
for (auto itBlock = F.begin(), endBlock = F.end(); itBlock != endBlock;
++itBlock) {
for (auto itInst = itBlock->begin(), endInst = itBlock->end();
itInst != endInst; ++itInst) {
Instruction *I = itInst;
++counters.insts;
if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) {
Type *pTy = AI->getType()->getPointerElementType();
// Count number of bytes used in alloca arrays.
if (pTy->isArrayTy()) {
counters.array_local_bytes += DL.getTypeAllocSize(pTy);
}
} else if (CallInst *CI = dyn_cast<CallInst>(I)) {
if (hlsl::OP::IsDxilOpFuncCallInst(CI)) {
unsigned opcode = static_cast<unsigned>(hlsl::OP::getOpCode(CI));
CountDxilOp(opcode, counters);
}
} else if (isa<LoadInst>(I) || isa<StoreInst>(I)) {
LoadInst *LI = dyn_cast<LoadInst>(I);
StoreInst *SI = dyn_cast<StoreInst>(I);
Value *PtrOp = LI ? LI->getPointerOperand() : SI->getPointerOperand();
PointerInfo PI = GetPointerInfo(PtrOp, ptrInfoMap);
// Count load/store on array elements.
if (PI.isArray) {
switch (PI.memType) {
case PointerInfo::MemType::Alloca:
++counters.array_local_ldst;
break;
case PointerInfo::MemType::Global_Static:
++counters.array_static_ldst;
break;
case PointerInfo::MemType::Global_TGSM:
++counters.array_tgsm_ldst;
break;
default:
break;
}
}
} else if (BranchInst *BI = dyn_cast<BranchInst>(I)) {
if (BI->getNumSuccessors() > 1) {
// TODO: More sophisticated analysis to separate dynamic from static
// branching?
++counters.branches;
}
} else {
// Count llvm ops:
CountLlvmOp(I->getOpcode(), counters);
}
}
}
}
}
struct CounterOffsetByName {
StringRef name;
uint32_t DxilCounters::*ptr;
};
// Must be sorted case-sensitive:
static const CounterOffsetByName CountersByName[] = {
// clang-format off
// Python lines need to be not formatted.
// <py::lines('COUNTER-MEMBER-PTRS')>['{ "%s", &DxilCounters::%s },' % (c,c) for c in hctdb_instrhelp.get_counters()]</py>
// clang-format on
// COUNTER-MEMBER-PTRS:BEGIN
{"array_local_bytes", &DxilCounters::array_local_bytes},
{"array_local_ldst", &DxilCounters::array_local_ldst},
{"array_static_bytes", &DxilCounters::array_static_bytes},
{"array_static_ldst", &DxilCounters::array_static_ldst},
{"array_tgsm_bytes", &DxilCounters::array_tgsm_bytes},
{"array_tgsm_ldst", &DxilCounters::array_tgsm_ldst},
{"atomic", &DxilCounters::atomic},
{"barrier", &DxilCounters::barrier},
{"branches", &DxilCounters::branches},
{"fence", &DxilCounters::fence},
{"floats", &DxilCounters::floats},
{"gs_cut", &DxilCounters::gs_cut},
{"gs_emit", &DxilCounters::gs_emit},
{"insts", &DxilCounters::insts},
{"ints", &DxilCounters::ints},
{"sig_ld", &DxilCounters::sig_ld},
{"sig_st", &DxilCounters::sig_st},
{"tex_bias", &DxilCounters::tex_bias},
{"tex_cmp", &DxilCounters::tex_cmp},
{"tex_grad", &DxilCounters::tex_grad},
{"tex_load", &DxilCounters::tex_load},
{"tex_norm", &DxilCounters::tex_norm},
{"tex_store", &DxilCounters::tex_store},
{"uints", &DxilCounters::uints},
// COUNTER-MEMBER-PTRS:END
};
static int CounterOffsetByNameLess(const CounterOffsetByName &a,
const CounterOffsetByName &b) {
return a.name < b.name;
}
uint32_t *LookupByName(llvm::StringRef name, DxilCounters &counters) {
CounterOffsetByName key = {name, nullptr};
static const CounterOffsetByName *CounterEnd =
CountersByName + _countof(CountersByName);
auto result = std::lower_bound(CountersByName, CounterEnd, key,
CounterOffsetByNameLess);
if (result != CounterEnd && result->name == key.name)
return &(counters.*(result->ptr));
return nullptr;
}
} // namespace hlsl