#include "gemmstone/strategy_parser.hpp"
#include "gemmstone/kernel_catalog.hpp"
#include "internal/utils.hpp"
#include "pieces/layout_utils.hpp"
#include <cctype>
#include <sstream>
GEMMSTONE_NAMESPACE_START
using namespace ngen;
static void unparseAccessType(std::ostream &s, const MatrixAddressingStrategy &astrategy);
static void unparseAddressBase(std::ostream &s, ngen::AddressBase base);
static void unparseCaching(HW hw, std::ostream &s, const MatrixAddressingStrategy &astrategy);
static void unparseTiling(std::ostream &s, const MatrixAddressingStrategy &astrategy);
bool native64Bit(HW hw)
{
EmulationStrategy emulate(hw);
return !emulate.emulate64;
}
AccessType getAccessType(char c)
{
switch (std::tolower(c)) {
case 'b': return AccessType::Block;
case 'p': return AccessType::PseudoBlock;
case 's': return AccessType::Scattered;
case 'u': return AccessType::ChannelScattered;
case 'm': return AccessType::Block2D;
case 't': return AccessType::Block2DTranspose;
case 'v': return AccessType::Block2DVNNI;
case 'c': return AccessType::CacheLine;
default: stub("Unknown access type.");
}
}
char downgradeBlock2D(char c)
{
switch (std::tolower(c)) {
case 'm':
case 'v': return 'b';
case 't': return 's';
default: return c;
}
}
AddressBase getAddressBase(char c)
{
switch (c) {
case 'a': return AddressBase::createA64(true);
case 'c': return AddressBase::createCC(0);
case 'l': return AddressBase::createSLM();
case 'm': return AddressBase::createSC(0);
case 'r': return AddressBase{};
case 's': return AddressBase::createBTS(0);
default: stub("Unknown address space.");
}
}
CacheSettingsLSC getCaching(char l1, char l3)
{
if (l1 == 'd' && l3 == 'd')
return CacheSettingsLSC::Default;
bool l3cached = (l3 == 'c') || (l3 == 'b');
switch (l1) {
case 'u': return l3cached ? CacheSettingsLSC::L1UC_L3C : CacheSettingsLSC::L1UC_L3UC;
case 't':
case 'c': return l3cached ? CacheSettingsLSC::L1C_L3C : CacheSettingsLSC::L1C_L3UC;
case 's': return l3cached ? CacheSettingsLSC::L1S_L3C : CacheSettingsLSC::L1S_L3UC;
case 'b':
case 'i': return CacheSettingsLSC::L1IAR_L3C; break;
default: stub("Unknown cache setting");
}
}
CacheSettingsLSC getCaching(char l1, char l2, char l3) {
if (l3 == 'u' || l3 == 'i') return getCaching(l1, l2);
if (l3 == 'c' || l3 == 'b') {
bool l2cached = (l2 == 'c') || (l2 == 'b');
switch (l1) {
case 'u':
return l2cached ? CacheSettingsLSC::L1UC_L2C_L3C
: CacheSettingsLSC::L1UC_L2UC_L3C;
case 't':
case 'c':
return l2cached ? CacheSettingsLSC::L1C_L2C_L3C
: CacheSettingsLSC::L1C_L2UC_L3C;
case 's':
return l2cached ? CacheSettingsLSC::L1S_L2C_L3C
: CacheSettingsLSC::L1S_L2UC_L3C;
case 'b':
if (!l2cached) return CacheSettingsLSC::L1WB_L2UC_L3WB;
default: break;
}
}
throw std::runtime_error("Unknown cache setting");
}
CacheSettingsLSC getCachingEntry(std::stringstream &s, HW hw)
{
if (hw >= HW::XE3P_35_10) {
char l1, l2, l3;
s >> l1 >> l2 >> l3;
return getCaching(l1, l2, l3);
} else
{
char l1, l3;
s >> l1 >> l3;
return getCaching(l1, l3);
}
}
void getCaching(std::stringstream &s, HW hw, MatrixAddressingStrategy &astrategy, bool leaveDefault = false)
{
auto &cachingR = astrategy.cachingR;
auto &cachingW = astrategy.cachingW;
if (!leaveDefault) {
cachingR = CacheSettingsLSC::L1C_L3C;
cachingW = CacheSettingsLSC::L1WB_L3WB;
if (hw >= HW::XeHPC)
cachingW = CacheSettingsLSC::L1UC_L3WB;
if (hw >= HW::XE3P_35_10)
cachingR = CacheSettingsLSC::L1C_L2C_L3C;
}
if (s.peek() == '{') {
char eat;
s >> eat;
cachingR = getCachingEntry(s, hw);
s >> eat;
if (eat == '/') {
cachingW = getCachingEntry(s, hw);
s >> eat;
}
if (eat != '}')
stub("Invalid caching syntax");
}
}
static void getTiling(std::stringstream &s, MatrixAddressingStrategy &astrategy)
{
if (s.peek() == '#') {
char eat = 0;
int in = 0;
s >> eat >> in, astrategy.tileR = in;
s >> eat >> in, astrategy.tileC = in;
}
}
void parseStrategy(const std::string &str, HW hw, const GEMMProblem &problem, GEMMStrategy &strategy)
{
std::stringstream s(str);
s.imbue(std::locale::classic());
bool overrideFusedLoop = false;
bool gotSR = false;
char eat, asA, asB, asC, accessA, accessB, accessC;
char accessAUnaligned = '\0', accessBUnaligned = '\0';
char accessAPrefetch = 's', accessBPrefetch = 's', accessCPrefetch = 's';
char accessABPrefetchL3 = 'b';
auto A64 = AddressBase::createA64(true);
auto BTS = AddressBase::createBTS(0);
s >> std::ws >> asA >> accessA;
if (s.peek() == '/') s >> eat >> accessAUnaligned;
s >> strategy.ka_load;
if (s.peek() == '/') s >> eat >> strategy.ka_load_masked;
getTiling(s, strategy.A);
if (s.peek() == 'x') s >> eat >> strategy.A_copies;
getCaching(s, hw, strategy.A);
if (s.peek() == '+') {
s >> eat;
if (s.peek() != '+') {
strategy.prefetchA = 1;
s >> accessAPrefetch >> strategy.ka_prefetch;
if (s.peek() == ',') s >> eat >> strategy.ka_pfStride;
if (s.peek() == '@') s >> eat >> strategy.prefetchA;
if (s.peek() == '/') s >> eat >> strategy.prefetchAMasked;
else strategy.prefetchAMasked = strategy.prefetchA;
getCaching(s, hw, strategy.A_prefetch);
}
}
if (s.peek() == '+') {
strategy.l3PrefetchA = true;
s >> eat >> accessABPrefetchL3 >> strategy.ka_prefetchL3;
if (s.peek() == '@') s >> eat >> strategy.prefetchABL3;
getCaching(s, hw, strategy.AB_prefetchL3, true);
}
s >> std::ws >> asB >> accessB;
if (s.peek() == '/') s >> eat >> accessBUnaligned;
s >> strategy.kb_load;
if (s.peek() == '/') s >> eat >> strategy.kb_load_masked;
getTiling(s, strategy.B);
if (s.peek() == 'x') s >> eat >> strategy.B_copies;
getCaching(s, hw, strategy.B);
if (s.peek() == '+') {
s >> eat;
if (s.peek() != '+') {
strategy.prefetchB = 1;
s >> accessBPrefetch >> strategy.kb_prefetch;
if (s.peek() == ',') s >> eat >> strategy.kb_pfStride;
if (s.peek() == '@') s >> eat >> strategy.prefetchB;
if (s.peek() == '/') s >> eat >> strategy.prefetchBMasked;
else strategy.prefetchBMasked = strategy.prefetchB;
getCaching(s, hw, strategy.B_prefetch);
}
}
if (s.peek() == '+') {
strategy.l3PrefetchB = true;
s >> eat >> accessABPrefetchL3 >> strategy.kb_prefetchL3;
if (s.peek() == '@') s >> eat >> strategy.prefetchABL3;
getCaching(s, hw, strategy.AB_prefetchL3, true);
}
s >> std::ws >> asC >> accessC;
getTiling(s, strategy.C);
getCaching(s, hw, strategy.C);
if (s.peek() == '+') {
strategy.prefetchC = 1;
s >> eat >> accessCPrefetch;
if (s.peek() == '@') s >> eat >> strategy.prefetchC;
getCaching(s, hw, strategy.C_prefetch);
}
if (!accessAUnaligned) accessAUnaligned = downgradeBlock2D(accessA);
if (!accessBUnaligned) accessBUnaligned = downgradeBlock2D(accessB);
strategy.A.base = strategy.A_prefetch.base = getAddressBase(asA);
strategy.B.base = strategy.B_prefetch.base = getAddressBase(asB);
strategy.C.base = strategy.C_prefetch.base = getAddressBase(asC);
strategy.CO.base = (hw >= HW::XeHPG) ? A64 : BTS;
strategy.A.newDP = bool(std::isupper(accessA));
strategy.B.newDP = bool(std::isupper(accessB));
strategy.C.newDP = bool(std::isupper(accessC));
strategy.CO.newDP = strategy.C.newDP;
strategy.A.accessType = getAccessType(accessA);
strategy.B.accessType = getAccessType(accessB);
strategy.C.accessType = getAccessType(accessC);
strategy.unalignedAccA = getAccessType(accessAUnaligned);
strategy.unalignedAccB = getAccessType(accessBUnaligned);
strategy.A.cachingW = CacheSettingsLSC::Default;
strategy.B.cachingW = CacheSettingsLSC::Default;
strategy.CO.cachingR = CacheSettingsLSC::L1C_L3C;
if (hw >= HW::XE3P_35_10) strategy.CO.cachingR = CacheSettingsLSC::L1C_L2C_L3C;
strategy.A_prefetch.prefetch = true;
strategy.B_prefetch.prefetch = true;
strategy.C_prefetch.prefetch = true;
strategy.A_prefetch.newDP = bool(std::isupper(accessAPrefetch));
strategy.B_prefetch.newDP = bool(std::isupper(accessBPrefetch));
strategy.C_prefetch.newDP = bool(std::isupper(accessCPrefetch));
strategy.A_prefetch.accessType = getAccessType(accessAPrefetch);
strategy.B_prefetch.accessType = getAccessType(accessBPrefetch);
strategy.C_prefetch.accessType = getAccessType(accessCPrefetch);
strategy.A_prefetch.cachingW = CacheSettingsLSC::Default;
strategy.B_prefetch.cachingW = CacheSettingsLSC::Default;
strategy.C_prefetch.cachingW = CacheSettingsLSC::Default;
strategy.AB_prefetchL3.prefetch = true;
strategy.AB_prefetchL3.newDP = true;
strategy.AB_prefetchL3.padded = true;
strategy.AB_prefetchL3.accessType = getAccessType(accessABPrefetchL3);
strategy.AB_prefetchL3.base = getAddressBase(strategy.l3PrefetchA ? asA : asB);
if (strategy.AB_prefetchL3.cachingR == CacheSettingsLSC::Default) {
strategy.AB_prefetchL3.cachingR = CacheSettingsLSC::L1UC_L3C;
if (hw >= HW::XE3P_35_10)
strategy.AB_prefetchL3.cachingR = CacheSettingsLSC::L1UC_L2C_L3C;
}
strategy.A.padded |= isPacked(problem.A.layout);
strategy.B.padded |= isPacked(problem.B.layout);
strategy.A_prefetch.padded |= isPacked(problem.A.layout);
strategy.B_prefetch.padded |= isPacked(problem.B.layout);
strategy.unroll[LoopK] = 1;
strategy.checkAdd32 = !native64Bit(hw) || (hw == HW::XeHPC);
strategy.altCRemainder |= (strategy.C.accessType == AccessType::Block) || strategy.kParallel;
while (!s.eof()) {
std::string mod;
s >> mod;
if (mod == "cs")
strategy.registerScheme = GEMMStrategy::CSeparate;
else if (mod == "acb")
strategy.registerScheme = GEMMStrategy::ACB;
else if (mod == "bca")
strategy.registerScheme = GEMMStrategy::BCA;
else if (mod == "vnc")
strategy.registerScheme = GEMMStrategy::VNC;
else if (mod == "int")
strategy.registerScheme = GEMMStrategy::ABInterleave;
else if (mod == "nse")
strategy.registerScheme = GEMMStrategy::NSeparate;
else if (mod == "vav")
strategy.registerScheme = GEMMStrategy::VAvoid;
else if (mod.substr(0, 3) == "grf") {
mod.erase(0,3);
strategy.GRFs = std::stoi(mod);
} else if (mod == "dsl")
strategy.isDSLGenerator = true;
else if (mod.substr(0, 3) == "dot") {
mod.erase(0,3);
strategy.dotVL = mod.empty() ? 1 : std::stoi(mod);
} else if (mod.substr(0, 3) == "cti") {
mod.erase(0,3);
strategy.cInterleaveChunk = mod.empty() ? 64 / problem.Tc_ext : std::stoi(mod);
} else if (mod == "sys")
strategy.systolic = true;
else if (mod == "dw")
strategy.dpasw = true;
else if (mod == "fs") {
strategy.fixedSystolic = strategy.systolic = true;
strategy.CO.base = BTS;
} else if (mod == "ar")
strategy.altCRemainder = true;
else if (mod == "sr") {
strategy.altCRemainder = false;
gotSR = true;
} else if (mod == "br")
strategy.block2DCRemainder = true;
else if (mod == "bf")
strategy.block2DCFull = strategy.block2DCRemainder = true;
else if (mod == "ac")
strategy.cAccumulators = true;
else if (mod == "el")
strategy.cLoadAhead = true;
else if (mod == "di")
strategy.delayABInc = true;
else if (mod == "ba")
strategy.loadBFirst = true;
else if (mod == "dm")
strategy.doubleMasking = true;
else if (mod == "kd")
strategy.kDescRem = true;
else if (mod == "sc")
strategy.splitCopy = true;
else if (mod == "sm")
strategy.coopA = CoopSplit::MN;
else if (mod == "ska")
strategy.coopA = CoopSplit::FullK;
else if (mod == "sn")
strategy.coopB = CoopSplit::MN;
else if (mod == "skb")
strategy.coopB = CoopSplit::FullK;
else if (mod == "ni")
strategy.slmUseIncrCopy = false;
else if (mod == "ek")
strategy.slmEarlyKMask = true;
else if (mod == "sf")
strategy.strictFence = true;
else if (mod == "ta")
strategy.slmATrans = true;
else if (mod == "tb")
strategy.slmBTrans = true;
else if (mod == "af")
strategy.atomicFMA = true;
else if (mod == "xaf")
strategy.atomicFMA = strategy.extendedAtomicFMA = true;
else if (mod == "st")
strategy.stallAfterLoad = true;
else if (mod == "fx")
strategy.fmaBoustrophedon = true;
else if (mod == "ch")
strategy.checkAdd32 = true;
else if (mod == "nch")
strategy.checkAdd32 = false;
else if (mod == "ws")
strategy.wgInSS = true;
else if (mod == "wc")
strategy.C.smode = ScatterSIMD::Wide;
else if (mod == "cc")
strategy.forceCopyC = true;
else if (mod == "njs")
strategy.jointSplit = false;
else if (mod == "np") {
strategy.A.padded = strategy.A_prefetch.padded = false;
strategy.B.padded = strategy.B_prefetch.padded = false;
} else if (mod == "pab") {
strategy.A.padded = strategy.A_prefetch.padded = true;
strategy.B.padded = strategy.B_prefetch.padded = true;
} else if (mod == "pc")
strategy.C.padded = strategy.C_prefetch.padded = true;
else if (mod == "up")
strategy.panelCheck = true;
else if (mod == "mnk") {
strategy.loopOrder[0] = LoopM;
strategy.loopOrder[1] = LoopN;
strategy.loopOrder[2] = LoopK;
} else if (mod == "nmk") {
strategy.loopOrder[0] = LoopN;
strategy.loopOrder[1] = LoopM;
strategy.loopOrder[2] = LoopK;
} else if (mod == "fm") {
strategy.fusedLoop = LoopM;
overrideFusedLoop = true;
} else if (mod == "fn") {
strategy.fusedLoop = LoopN;
overrideFusedLoop = true;
} else if (mod == "rm")
strategy.reverse[LoopM] = true;
else if (mod == "rn")
strategy.reverse[LoopN] = true;
else if (mod == "ym")
strategy.scramble[LoopM] = true;
else if (mod == "yn")
strategy.scramble[LoopN] = true;
else if (mod == "wt")
strategy.tlbWarmup = true;
else if (mod == "kb" || mod == "kv") {
if (mod == "kb") strategy.kParallel = true;
if (mod == "kv") {
strategy.kParallelVariable = true;
strategy.fuseBeta = true;
strategy.fusePostOps = true;
}
strategy.C.atomic = true;
strategy.CO.atomic = problem.sumA || problem.sumB;
if (strategy.CO.atomic)
strategy.CO.base = A64;
} else if (mod == "kr")
strategy.kParallelLocal = true;
else if (mod == "akr")
strategy.kParallelLocal = strategy.shrinkWGK = true;
else if (mod == "ikr")
strategy.kParallelLocal = strategy.kInterleave = true;
else if (mod == "fb")
strategy.fuseBeta = true;
else if (mod == "afb")
strategy.fuseBeta = strategy.altFusedBeta = true;
else if (mod == "fp")
strategy.fusePostOps = true;
else if (mod == "zt")
strategy.zeroTempC = true;
else if (mod == "rx")
strategy.relaxedAccumulation = true;
else if (mod == "fg") {
float fillGoal;
s >> fillGoal;
strategy.fillGoal = int(fillGoal * 16);
} else if (mod == "au")
strategy.C.atomic = strategy.CO.atomic = true;
else if (mod == "nau")
strategy.C.atomic = strategy.CO.atomic = strategy.autoatomic = false;
else if (mod == "ff")
strategy.forceWGUpdate = WGFixed;
else if (mod == "ffk")
strategy.forceFixedWGK = true;
else if (mod == "wg") {
char x;
s >> strategy.wg[LoopM];
s >> x;
s >> strategy.wg[LoopN];
strategy.wg[LoopK] = 0;
if (s.peek() == 'x')
s >> x >> strategy.wg[LoopK];
} else if (mod == "nb") {
char x;
s >> strategy.namedBarriers[LoopM];
s >> std::ws >> x;
s >> strategy.namedBarriers[LoopN];
} else if (mod == "bo")
strategy.cWalkOrder = WalkOrder::Boustrophedon;
else if (mod == "hi")
strategy.cWalkOrder = WalkOrder::Hilbertlike;
else if (mod == "li")
strategy.cWalkOrder = WalkOrder::SimpleLinear;
else if (mod == "nl")
strategy.cWalkOrder = WalkOrder::NestedLinear;
else if (mod == "pt")
strategy.persistent = true;
else if (mod == "of")
strategy.arbitrationMode = ThreadArbitrationMode::OldestFirst;
else if (mod == "rr")
strategy.arbitrationMode = ThreadArbitrationMode::RoundRobin;
else if (mod == "rrs")
strategy.arbitrationMode = ThreadArbitrationMode::RoundRobinOnStall;
else if (mod == "l2d")
strategy.optAlignAB2D = true;
else if (mod == "nq") {
strategy.A.noExtraPad = strategy.A_prefetch.noExtraPad = true;
strategy.B.noExtraPad = strategy.B_prefetch.noExtraPad = true;
strategy.C.noExtraPad = strategy.C_prefetch.noExtraPad = true;
strategy.CO.noExtraPad = true;
} else if (mod.length() >= 2) {
if (mod.substr(0, 2) == "ms")
strategy.mSplitThresh = stoi(mod.substr(2));
else if (mod.substr(0, 2) == "ns")
strategy.nSplitThresh = stoi(mod.substr(2));
else if (mod.substr(0, 2) == "ki")
strategy.kInterleaveChunk = stoi(mod.substr(2));
else if (mod.substr(0, 2) == "kc")
strategy.kChain = stoi(mod.substr(2));
else if (mod.substr(0, 2) == "ks") {
char eat;
std::stringstream ms(mod);
ms.imbue(std::locale::classic());
ms >> eat >> eat >> strategy.unrollKSLM;
if (!ms.eof() && (ms.peek() == '/'))
ms >> eat >> strategy.unrollKSLMMasked;
} else if (mod.substr(0, 2) == "sb") {
strategy.barrierFreq = stoi(mod.substr(2));
strategy.splitBarrier = true;
} else if (mod.substr(0, 2) == "pk")
strategy.kPadding = stoi(mod.substr(2));
else if (mod.substr(0, 2) == "cr") {
strategy.cRepackPanel = stoi(mod.substr(2));
if (strategy.cRepackPanel == 0)
strategy.cRepackPanel = 1024;
} else if (mod.substr(0, 2) == "rc") {
strategy.repackC = stoi(mod.substr(2));
if (strategy.cRepackPanel == 0)
strategy.cRepackPanel = 1024;
} else if (mod.substr(0, 2) == "wx") {
strategy.wgPadFactor = stoi(mod.substr(2));
strategy.forceWGUpdate = WGFixed;
} else if (mod.substr(0, 2) == "ql") {
strategy.skewLocalIDs = true;
} else switch (mod[0]) {
case 'b':
if (isdigit(mod[1]))
strategy.barrierFreq = stoi(mod.substr(1));
else {
LoopType loop;
switch (mod[1]) {
case 'm': loop = LoopM; break;
case 'n': loop = LoopN; break;
case 'k': loop = LoopK; break;
default: stub("Unknown strategy modifier.");
}
size_t alt;
strategy.blocking[loop] = stoi(mod.substr(2), &alt);
if (strategy.blocking[loop] == 0)
strategy.blocking[loop] = 16777216;
alt += 3;
if (mod.length() > alt)
strategy.blockingAlt[loop] = stoi(mod.substr(alt));
}
break;
case 'c': {
mod.erase(0, 1);
if (mod[0] == 'a') {
mod.erase(0, 1);
strategy.slmA = true;
}
if (mod[0] == 'b') {
mod.erase(0, 1);
strategy.slmB = true;
}
std::stringstream ms(mod);
ms.imbue(std::locale::classic());
ms >> strategy.slmBuffers;
ms >> eat;
if (!ms.eof())
ms >> strategy.slmCopies;
break;
}
case 'k': {
char eat;
std::stringstream ms(mod);
ms.imbue(std::locale::classic());
ms >> eat >> strategy.unroll[LoopK];
if (!ms.eof() && (ms.peek() == '/'))
ms >> eat >> strategy.unrollK_masked;
strategy.extraKAlign = strategy.unroll[LoopK];
break;
} case 'l':
strategy.optAlignAB = stoi(mod.substr(1));
break;
default:
stub("Unknown strategy modifier.");
}
} else if (!mod.empty())
stub("Unknown strategy modifier.");
}
if (!overrideFusedLoop) {
if (strategy.fused) {
if (strategy.wg[LoopM] == 1)
strategy.fusedLoop = LoopN;
else if (strategy.wg[LoopN] == 1)
strategy.fusedLoop = LoopM;
else
strategy.fusedLoop = strategy.loopOrder[0];
} else
strategy.fusedLoop = strategy.loopOrder[0];
}
if (strategy.ka_pfStride == 0) strategy.ka_pfStride = strategy.ka_prefetch;
if (strategy.kb_pfStride == 0) strategy.kb_pfStride = strategy.kb_prefetch;
if (strategy.block2DCRemainder && !gotSR) strategy.altCRemainder = true;
if (strategy.persistentLoop() && !strategy.linearOrder()) strategy.cWalkOrder = WalkOrder::SimpleLinear;
size_t poCount = problem.postOps.len();
strategy.binary.resize(poCount);
for (auto &astrategy: strategy.binary) {
astrategy.base = (hw >= HW::XeHPC) ? A64 : BTS;
astrategy.newDP = strategy.C.newDP;
}
bool surfaceAq = ((problem.quantized2DA() || problem.needsAGroupSums()) && !strategy.A.base.isStateless());
bool surfaceBq = ((problem.quantized2DB() || problem.needsBGroupSums()) && !strategy.B.base.isStateless());
bool surfaceAO = surfaceAq && problem.aOffset2D();
bool surfaceBO = surfaceBq && problem.bOffset2D();
strategy.AO.base = (surfaceAO ? BTS : A64);
strategy.BO.base = (surfaceBO ? BTS : A64);
strategy.Ag.base = strategy.A_scale.base = (surfaceAq ? BTS : A64);
strategy.Bg.base = strategy.B_scale.base = (surfaceBq ? BTS : A64);
strategy.AO.newDP = strategy.A_scale.newDP = strategy.Ag.newDP = (hw >= HW::XeHPG);
strategy.BO.newDP = strategy.B_scale.newDP = strategy.Bg.newDP = (hw >= HW::XeHPG);
}
void adjustStrategy(HW hw, const GEMMProblem &problem, GEMMStrategy &strategy, const char *tags)
{
auto *gemmAStrategy = &strategy.A, *gemmBStrategy = &strategy.B;
if (problem.A.needA64) strategy.A.forceA64();
if (problem.B.needA64) strategy.B.forceA64();
if (problem.C.needA64) strategy.C.forceA64();
auto use2DAddressing = [](MatrixAddressingStrategy &astrategy) {
astrategy.address2D |= isBlock2D(astrategy.accessType);
};
if (!isPacked(problem.A.layout)) {
use2DAddressing(strategy.A);
use2DAddressing(strategy.A_prefetch);
}
if (!isPacked(problem.B.layout)) {
use2DAddressing(strategy.B);
use2DAddressing(strategy.B_prefetch);
}
if (!isPacked(problem.C.layout)) {
use2DAddressing(strategy.C);
use2DAddressing(strategy.C_prefetch);
}
if (!(strategy.l3PrefetchA && isPacked(problem.A.layout)) && !(strategy.l3PrefetchB && isPacked(problem.B.layout)))
use2DAddressing(strategy.AB_prefetchL3);
if (tags && !strategy.optAlignAB2D) {
bool block2DA = false, block2DB = false;
while (*tags) {
block2DA |= (*tags == kcatalog::ReqBlock2DA);
block2DB |= (*tags == kcatalog::ReqBlock2DB);
tags++;
}
strategy.A_prefetch.preflight(hw);
strategy.B_prefetch.preflight(hw);
if (!block2DA && strategy.A_prefetch.address2D)
strategy.A_prefetch.address2D = false;
if (!block2DB && strategy.B_prefetch.address2D)
strategy.B_prefetch.address2D = false;
}
if (isBlock2D(strategy.A.accessType) && (!strategy.prefetchA || isBlock2D(strategy.A_prefetch.accessType)))
if (!problem.quantized2DA() && !problem.needsAGroupSums())
strategy.remHandling[LoopM] = RemainderHandling::General;
if (isBlock2D(strategy.B.accessType) && (!strategy.prefetchB || isBlock2D(strategy.B_prefetch.accessType)))
if (!problem.quantized2DB() && !problem.needsBGroupSums())
strategy.remHandling[LoopN] = RemainderHandling::General;
if (gemmAStrategy->padded) strategy.remHandling[LoopM] = RemainderHandling::General;
if (gemmBStrategy->padded) strategy.remHandling[LoopN] = RemainderHandling::General;
if (strategy.prefetchC && !isBlock2D(strategy.C_prefetch.accessType))
strategy.remHandling[LoopM] = strategy.remHandling[LoopN] = RemainderHandling::Split;
if (strategy.panelCheck && strategy.lateExit() && hw >= HW::XeHP && !strategy.fixedSystolic) {
if (isPacked(problem.A.layout)) strategy.remHandling[LoopM] = RemainderHandling::Split;
if (isPacked(problem.B.layout)) strategy.remHandling[LoopN] = RemainderHandling::Split;
}
if (strategy.unroll[LoopM] * strategy.wg[LoopM] == 1) strategy.remHandling[LoopM] = RemainderHandling::Ignore;
if (strategy.unroll[LoopN] * strategy.wg[LoopN] == 1) strategy.remHandling[LoopN] = RemainderHandling::Ignore;
}
const char *parseLayout(const char *s, MatrixAddressing &atype)
{
atype.crosspack = 1;
atype.alignment = 0;
switch (*s++) {
case 'A': atype.layout = MatrixLayout::Pc; break;
case 'B': atype.layout = MatrixLayout::Pr; break;
case 'N': atype.layout = MatrixLayout::N; break;
case 'T': atype.layout = MatrixLayout::T; break;
case '?': atype.layout = MatrixLayout::N; break; default:
throw std::runtime_error("Unknown matrix layout requested.");
}
if (isdigit(*s))
atype.crosspack = (uint8_t)strtol(s, (char **) &s, 10);
if (*s == '#') {
s++;
atype.tileR = (uint16_t)strtol(s, (char **) &s, 10);
if (*s != ',') throw std::runtime_error("Bad tiling syntax; expected #<rows>,<columns>.");
s++;
atype.tileC = (uint16_t)strtol(s, (char **) &s, 10);
}
if (*s == '%') {
s++;
atype.packSize = (uint32_t)strtol(s, (char **) &s, 10);
}
if (*s == '@') {
s++;
atype.alignment = (uint8_t)strtol(s, (char **) &s, 10);
}
return s;
}
const char *parsePrecision(const char *s, Type &precision)
{
if (*s) {
precision = charPrecision(*s++);
}
return s;
}
const char *parsePrecisions(const char *s, Type &precision1, Type &precision2)
{
if (*s == '[') {
s++;
s = parsePrecision(s, precision1);
s = parsePrecision(s, precision2);
if (*s++ != ']')
throw std::runtime_error("Syntax error in precisions; expected ]");
} else {
s = parsePrecision(s, precision1);
precision2 = precision1;
}
return s;
}
std::string unparseStrategy(HW hw, const GEMMProblem &problem, const GEMMStrategy &strategy)
{
std::stringstream s;
s.imbue(std::locale::classic());
bool anyOptAlignAB = strategy.optAlignAB || strategy.optAlignAB2D;
unparseAddressBase(s, strategy.A.base);
unparseAccessType(s, strategy.A);
if (anyOptAlignAB && strategy.unalignedAccA != strategy.A.accessType) {
auto unalignedA = strategy.A;
unalignedA.accessType = strategy.unalignedAccA;
s << '/';
unparseAccessType(s, unalignedA);
}
s << strategy.ka_load;
if (strategy.ka_load_masked != strategy.ka_load)
s << '/' << strategy.ka_load_masked;
unparseTiling(s, strategy.A);
if (strategy.A_copies > 1)
s << 'x' << strategy.A_copies;
unparseCaching(hw, s, strategy.A);
if (strategy.prefetchA) {
s << '+';
unparseAccessType(s, strategy.A_prefetch);
s << strategy.ka_prefetch << ',' << strategy.ka_pfStride << '@' << strategy.prefetchA;
if (strategy.prefetchAMasked != strategy.prefetchA)
s << '/' << strategy.prefetchAMasked;
unparseCaching(hw, s, strategy.A_prefetch);
}
if (strategy.l3PrefetchA) {
if (!strategy.prefetchA) s << '+';
s << '+';
unparseAccessType(s, strategy.AB_prefetchL3);
s << strategy.ka_prefetchL3 << '@' << strategy.prefetchABL3;
unparseCaching(hw, s, strategy.AB_prefetchL3);
}
s << ' ';
unparseAddressBase(s, strategy.B.base);
unparseAccessType(s, strategy.B);
if (anyOptAlignAB && strategy.unalignedAccB != strategy.B.accessType) {
auto unalignedB = strategy.B;
unalignedB.accessType = strategy.unalignedAccB;
s << '/';
unparseAccessType(s, unalignedB);
}
s << strategy.kb_load;
if (strategy.kb_load_masked != strategy.kb_load)
s << '/' << strategy.kb_load_masked;
unparseTiling(s, strategy.B);
if (strategy.B_copies > 1)
s << 'x' << strategy.B_copies;
unparseCaching(hw, s, strategy.B);
if (strategy.prefetchB) {
s << '+';
unparseAccessType(s, strategy.B_prefetch);
s << strategy.kb_prefetch << ',' << strategy.kb_pfStride << '@' << strategy.prefetchB;
if (strategy.prefetchBMasked != strategy.prefetchB)
s << '/' << strategy.prefetchBMasked;
unparseCaching(hw, s, strategy.B_prefetch);
}
if (strategy.l3PrefetchB) {
if (!strategy.prefetchB) s << '+';
s << '+';
unparseAccessType(s, strategy.AB_prefetchL3);
s << strategy.kb_prefetchL3 << '@' << strategy.prefetchABL3;
unparseCaching(hw, s, strategy.AB_prefetchL3);
}
s << ' ';
unparseAddressBase(s, strategy.C.base);
unparseAccessType(s, strategy.C);
unparseTiling(s, strategy.C);
unparseCaching(hw, s, strategy.C);
if (strategy.prefetchC) {
s << '+';
unparseAccessType(s, strategy.C_prefetch);
s << '@' << strategy.prefetchC;
unparseCaching(hw, s, strategy.C_prefetch);
}
if (strategy.slmA || strategy.slmB) {
s << " c";
if (strategy.slmA) s << 'a';
if (strategy.slmB) s << 'b';
s << strategy.slmBuffers;
if (strategy.slmCopies > 1)
s << 'x' << strategy.slmCopies;
if (strategy.unrollKSLM != 0) {
s << " ks" << strategy.unrollKSLM;
if (strategy.unrollKSLMMasked > 0 && strategy.unrollKSLMMasked != strategy.unrollKSLM)
s << '/' << strategy.unrollKSLMMasked;
}
}
if (strategy.wg[LoopM] != 0) {
s << " wg " << strategy.wg[LoopM] << 'x' << strategy.wg[LoopN];
if (strategy.wg[LoopK] > 1)
s << 'x' << strategy.wg[LoopK];
}
if (strategy.wgPadFactor > 1) s << " wx" << strategy.wgPadFactor;
if (strategy.forceWGUpdate == WGFixed) s << " ff";
if (strategy.kParallelLocal) s << (strategy.shrinkWGK ? " akr" :
strategy.kInterleave ? " ikr" :
" kr");
if (strategy.fillGoal) s << " fg" << strategy.fillGoal * (1. / 16);
if (strategy.kInterleave) s << " ki" << strategy.kInterleaveChunk;
if (strategy.fixedSystolic)
s << " fs";
else if (strategy.systolic) {
s << " sys";
if (strategy.dpasw)
s << " dw";
}
if (strategy.isDSLGenerator) s << " dsl";
if (strategy.dotVL) {
s << " dot";
if (strategy.dotVL > 1) s << strategy.dotVL;
}
if (strategy.kChain > 1) s << " kc" << strategy.kChain;
if (strategy.atomicFMA) s << (strategy.extendedAtomicFMA ? " xaf" : " af");
if (strategy.stallAfterLoad) s << " st";
if (strategy.fmaBoustrophedon) s << " fx";
s << " k" << strategy.unroll[LoopK];
if (strategy.GRFs != 128)
s << " grf" << strategy.GRFs;
if (strategy.coopA == CoopSplit::MN) s << " sm";
if (strategy.coopA == CoopSplit::FullK) s << " ska";
if (strategy.coopB == CoopSplit::MN) s << " sn";
if (strategy.coopB == CoopSplit::FullK) s << " skb";
switch (strategy.registerScheme) {
case GEMMStrategy::CSeparate: s << " cs"; break;
case GEMMStrategy::ACB: s << " acb"; break;
case GEMMStrategy::BCA: s << " bca"; break;
case GEMMStrategy::VNC: s << " vnc"; break;
case GEMMStrategy::ABInterleave: s << " int"; break;
case GEMMStrategy::NSeparate: s << " nse"; break;
case GEMMStrategy::VAvoid: s << " vav"; break;
}
if (strategy.cAccumulators) s << " ac";
if (strategy.cLoadAhead) s << " el";
if (strategy.delayABInc) s << " di";
if (strategy.loadBFirst) s << " ba";
if (strategy.doubleMasking) s << " dm";
if (strategy.kDescRem) s << " kd";
if (strategy.splitCopy) s << " sc";
if (!strategy.slmUseIncrCopy) s << " ni";
if (strategy.slmEarlyKMask) s << " ek";
if (strategy.strictFence) s << " sf";
if (strategy.slmATrans) s << " ta";
if (strategy.slmBTrans) s << " tb";
if (!strategy.altCRemainder) s << " sr";
else if (!strategy.block2DCRemainder) s << " ar";
if (strategy.block2DCFull) s << " bf";
else if (strategy.block2DCRemainder) s << " br";
if (strategy.panelCheck) s << " up";
if (strategy.reverse[LoopM]) s << " rm";
if (strategy.reverse[LoopN]) s << " rn";
if (strategy.scramble[LoopM]) s << " ym";
if (strategy.scramble[LoopN]) s << " yn";
if (strategy.tlbWarmup) s << " wt";
if (strategy.checkAdd32 && !strategy.emulate.emulate64) s << " ch";
if (!strategy.checkAdd32 && strategy.emulate.emulate64) s << " nch";
if (strategy.wgInSS) s << " ws";
if (strategy.C.smode == ScatterSIMD::Wide) s << " wc";
if (strategy.forceCopyC) s << " cc";
if (!strategy.jointSplit) s << " njs";
if (strategy.mSplitThresh) s << " ms" << strategy.mSplitThresh;
if (strategy.nSplitThresh) s << " ns" << strategy.nSplitThresh;
if (strategy.cInterleaveChunk > 1) s << " cti" << strategy.cInterleaveChunk;
if (strategy.kParallel) s << " kb";
if (strategy.kParallelVariable) s << " kv";
if (strategy.fuseBeta) s << (strategy.altFusedBeta ? " afb" : " fb");
if (strategy.fusePostOps) s << " fp";
if (strategy.zeroTempC) s << " zt";
if (strategy.relaxedAccumulation) s << " rx";
if (strategy.kPadding) s << " pk" << strategy.kPadding;
if (strategy.C.atomic && !strategy.kParallel && !strategy.kParallelVariable)
s << " au";
else if (!strategy.autoatomic)
s << " nau";
if (strategy.namedBarriers[LoopM] || strategy.namedBarriers[LoopN])
s << " nb " << strategy.namedBarriers[LoopM] << 'x' << strategy.namedBarriers[LoopN];
switch (strategy.cWalkOrder) {
case WalkOrder::Hilbertlike: s << " hi"; break;
case WalkOrder::Boustrophedon: s << " bo"; break;
case WalkOrder::SimpleLinear: s << " li"; break;
case WalkOrder::NestedLinear: s << " nl"; break;
default: break;
}
if (strategy.persistent)
s << " pt";
if (strategy.loopOrder[0] != LoopM || strategy.loopOrder[1] != LoopN || strategy.loopOrder[2] != LoopK) {
s << ' ';
for (int l = 0; l < 3; l++)
s << "mnk"[strategy.loopOrder[l]];
}
if (strategy.fused) s << " f" << "mnk"[strategy.fusedLoop];
switch (strategy.arbitrationMode) {
case ThreadArbitrationMode::OldestFirst: s << " of"; break;
case ThreadArbitrationMode::RoundRobin: s << " rr"; break;
case ThreadArbitrationMode::RoundRobinOnStall: s << " rrs"; break;
default: break;
}
if (strategy.optAlignAB > 0) s << " l" << strategy.optAlignAB;
if (strategy.optAlignAB2D) s << " l2d";
bool nq = false;
for (auto &astrategy: {strategy.A, strategy.B, strategy.C, strategy.CO,
strategy.A_prefetch, strategy.B_prefetch, strategy.C_prefetch}) {
nq |= astrategy.noExtraPad;
}
if (nq) s << " nq";
if (strategy.barrierFreq > 0) {
s << (strategy.splitBarrier ? " sb" : " b")
<< strategy.barrierFreq;
}
if (strategy.cRepackPanel) s << " cr" << strategy.cRepackPanel;
if (strategy.skewLocalIDs) {
s << " ql";
}
bool npA = !isPacked(problem.A.layout) && !strategy.A.padded;
bool npB = !isPacked(problem.B.layout) && !strategy.B.padded;
bool pabA = !isPacked(problem.A.layout) && strategy.A.padded;
bool pabB = !isPacked(problem.B.layout) && strategy.B.padded;
bool pc = !isPacked(problem.C.layout) && strategy.C.padded;
if (pabA || pabB) s << " pab";
if (npA || npB) s << " np";
if (pc) s << " pc";
for (auto loop: {LoopM, LoopN, LoopK}) if (strategy.blocking[loop] != 0) {
s << " b" << "mnk"[loop] << strategy.blocking[loop];
bool checkAlt = (loop == LoopK) ? (hw == HW::XeHP)
: (strategy.cWalkOrder == WalkOrder::Hilbertlike);
if (checkAlt && (strategy.blockingAlt[loop] != 0))
s << '/' << strategy.blockingAlt[loop];
}
return s.str();
}
void unparseAccessType(std::ostream &s, const MatrixAddressingStrategy &astrategy)
{
char c = ' ';
switch (astrategy.accessType) {
case AccessType::Block: c = 'b'; break;
case AccessType::PseudoBlock: c = 'p'; break;
case AccessType::Scattered: c = 's'; break;
case AccessType::ChannelScattered: c = 'u'; break;
case AccessType::Block2D: c = 'm'; break;
case AccessType::Block2DTranspose: c = 't'; break;
case AccessType::Block2DVNNI: c = 'v'; break;
case AccessType::CacheLine: c = 'c'; break;
}
if (astrategy.newDP)
c = std::toupper(c);
s << c;
}
void unparseAddressBase(std::ostream &s, ngen::AddressBase base)
{
switch (base.getModel()) {
case ngen::AddressModel::ModelA64: s << 'a'; break;
case ngen::AddressModel::ModelCC: s << 'c'; break;
case ngen::AddressModel::ModelSC: s << 'm'; break;
case ngen::AddressModel::ModelBTS: s << 's'; break;
case ngen::AddressModel::ModelInvalid: s << 'r'; break;
default: s << 'u'; break;
}
}
void unparseCaching(HW hw, std::ostream &s, const MatrixAddressingStrategy &astrategy)
{
auto cachingR = astrategy.cachingR;
auto cachingW = astrategy.cachingW;
if (!astrategy.newDP)
return;
if (cachingR == CacheSettingsLSC::Default && cachingW == CacheSettingsLSC::Default)
return;
s << '{';
if (hw >= HW::XE3P_35_10) {
switch (cachingR) {
case CacheSettingsLSC::Default: s << "ddd"; break;
case CacheSettingsLSC::L1UC_L2UC_L3UC: s << "uuu"; break;
case CacheSettingsLSC::L1UC_L2UC_L3C: s << "uuc"; break;
case CacheSettingsLSC::L1UC_L2C_L3UC: s << "ucu"; break;
case CacheSettingsLSC::L1UC_L2C_L3C: s << "ucc"; break;
case CacheSettingsLSC::L1C_L2UC_L3UC: s << "cuu"; break;
case CacheSettingsLSC::L1C_L2UC_L3C: s << "cuc"; break;
case CacheSettingsLSC::L1C_L2C_L3UC: s << "ccu"; break;
case CacheSettingsLSC::L1C_L2C_L3C: s << "ccc"; break;
case CacheSettingsLSC::L1S_L2UC_L3UC: s << "suu"; break;
case CacheSettingsLSC::L1S_L2UC_L3C: s << "suc"; break;
case CacheSettingsLSC::L1S_L2C_L3UC: s << "scu"; break;
case CacheSettingsLSC::L1S_L2C_L3C: s << "scc"; break;
case CacheSettingsLSC::L1IAR_L2IAR_L3IAR: s << "iii"; break;
default: s << "???"; break;
}
if (cachingW != CacheSettingsLSC::Default) {
s << '/';
switch (cachingW) {
case CacheSettingsLSC::L1UC_L2UC_L3UC: s << "uuu"; break;
case CacheSettingsLSC::L1UC_L2UC_L3WB: s << "uub"; break;
case CacheSettingsLSC::L1UC_L2WB_L3UC: s << "ubu"; break;
case CacheSettingsLSC::L1WT_L2UC_L3UC: s << "tuu"; break;
case CacheSettingsLSC::L1WT_L2UC_L3WB: s << "tub"; break;
case CacheSettingsLSC::L1WT_L2WB_L3UC: s << "tbu"; break;
case CacheSettingsLSC::L1S_L2UC_L3UC: s << "suu"; break;
case CacheSettingsLSC::L1S_L2UC_L3WB: s << "sub"; break;
case CacheSettingsLSC::L1S_L2WB_L3UC: s << "sbu"; break;
case CacheSettingsLSC::L1WB_L2WB_L3UC: s << "bbu"; break;
case CacheSettingsLSC::L1WB_L2UC_L3WB: s << "bub"; break;
default: s << "???"; break;
}
}
} else
{
switch (cachingR) {
case CacheSettingsLSC::Default: s << "dd"; break;
case CacheSettingsLSC::L1UC_L3UC: s << "uu"; break;
case CacheSettingsLSC::L1UC_L3C: s << "uc"; break;
case CacheSettingsLSC::L1C_L3UC: s << "cu"; break;
case CacheSettingsLSC::L1C_L3C: s << "cc"; break;
case CacheSettingsLSC::L1S_L3UC: s << "su"; break;
case CacheSettingsLSC::L1S_L3C: s << "sc"; break;
case CacheSettingsLSC::L1IAR_L3C: s << "ic"; break;
case CacheSettingsLSC::L1C_L3CC: s << "cn"; break;
case CacheSettingsLSC::L1UC_L3CC: s << "un"; break;
default: s << "??"; break;
}
if (cachingW != CacheSettingsLSC::Default) {
s << '/';
switch (cachingW) {
case CacheSettingsLSC::L1UC_L3UC: s << "uu"; break;
case CacheSettingsLSC::L1UC_L3C: s << "ub"; break;
case CacheSettingsLSC::L1C_L3UC: s << "tu"; break;
case CacheSettingsLSC::L1C_L3C: s << "tb"; break;
case CacheSettingsLSC::L1S_L3UC: s << "su"; break;
case CacheSettingsLSC::L1S_L3C: s << "sb"; break;
case CacheSettingsLSC::L1IAR_L3C: s << "bb"; break;
default: s << "??"; break;
}
}
}
s << '}';
}
void unparseTiling(std::ostream &s, const MatrixAddressingStrategy &astrategy)
{
if (astrategy.tileR || astrategy.tileC)
s << '#' << int(astrategy.tileR) << ',' << int(astrategy.tileC);
}
GEMMSTONE_NAMESPACE_END