#include "alloc_utils.hpp"
#include "gemmstone/generator.hpp"
#include "hw_utils.hpp"
#include "layout_utils.hpp"
#include "map.hpp"
#include "ngen_object_helpers.hpp"
#include "quantization.hpp"
GEMMSTONE_NAMESPACE_START
using namespace ngen;
using std::vector;
template <HW hw>
bool Generator<hw>::gemmMake2DQuantizationLayouts(bool isA, const GEMMProblem &problem, GEMMStrategy &strategy, GEMMState &state)
{
auto lateOffset = isA ? problem.needsBGroupSums() : problem.needsAGroupSums();
int xoPtrDims = (isA ? problem.aoPtrDims : problem.boPtrDims);
bool xo2D = isA ? problem.aOffset2D() : problem.bOffset2D();
bool xs2D = isA ? problem.aScale2D() : problem.bScale2D();
bool xg2D = isA ? problem.needsAGroupSums() : problem.needsBGroupSums();
bool xoTo2D = !xo2D && (isA ? problem.aOffset == ABOffset::Calc && (problem.earlyDequantizeA() || lateOffset)
: problem.bOffset == ABOffset::Calc && (problem.earlyDequantizeB() || lateOffset));
bool cColMajor = isRegisterColMajor(problem.Tc_ext, problem.C, strategy.C);
if (!xo2D && !xoTo2D && !xs2D && !xg2D) return true;
auto &X_strategy = isA ? strategy.A : strategy.B;
auto &X_offsetStrategy = isA ? strategy.AO : strategy.BO;
auto &X_scaleStrategy = isA ? strategy.A_scale : strategy.B_scale;
auto &Xg_strategy = isA ? strategy.Ag : strategy.Bg;
auto &X_offsetLayout = isA ? state.A_offsetLayout : state.B_offsetLayout;
auto &X_scaleLayout = isA ? state.A_scaleLayout : state.B_scaleLayout;
auto &Xg_layout = isA ? state.Ag_layout : state.Bg_layout;
auto &Xr_offsetLayout = isA ? state.Ar_offsetLayout : state.Br_offsetLayout;
auto &Xr_scaleLayout = isA ? state.Ar_scaleLayout : state.Br_scaleLayout;
auto &Xgr_layout = isA ? state.Agr_layout : state.Bgr_layout;
auto &XO = isA ? problem.AO : problem.BO;
auto &XS = isA ? problem.A_scale : problem.B_scale;
auto &Xg = isA ? problem.Ag : problem.Bg;
auto Tx_ext = isA ? problem.Ta_ext : problem.Tb_ext;
auto Tx = isA ? problem.Ta : problem.Tb;
auto Txo = isA ? problem.Tao : problem.Tbo;
auto Txs = isA ? problem.Ta_scale : problem.Tb_scale;
auto Txg = isA ? problem.Tag : problem.Tbg;
auto xqGroupK = isA ? problem.aqGroupK : problem.bqGroupK;
auto xqGroupMN = isA ? problem.aqGroupM : problem.bqGroupN;
auto &Txo_int = isA ? state.Tao_int : state.Tbo_int;
auto &Txs_int = isA ? state.Ta_scaleInt : state.Tb_scaleInt;
auto &Txg_int = isA ? state.Tag_int : state.Tbg_int;
auto &lateScale = isA ? state.lateScale2DA : state.lateScale2DB;
auto wgTileMN = strategy.wgTile(isA ? LoopM : LoopN);
Txo_int = Txo.isInteger() ? Tx.asSignedInt() : Tx;
Txs_int = Tx;
if (Tx == Type::bf16)
Txs_int = Type::f32;
Txg_int = Txg;
int cpoDiv = 1;
if (Txo_int.isInt8()) Txo_int = Type::s16, cpoDiv = 2;
if (xs2D && ((Txs.paddedSize() > Tx.paddedSize() && Tx.isInteger()) || problem.forceLateQuant(hw, minOuterProductCount(hw, problem, strategy)) || state.useBDPAS)) {
lateScale = true;
Txs_int = problem.Tc;
}
bool int4SpecialPath = Tx_ext.isInt4() && one_of(Tx, {Type::f16, Type::f32});
if (int4SpecialPath) {
Txo_int = Type::f16;
Txs_int = Tx;
if (Tx == Type::bf16) Txs_int = Type::f16;
}
if (lateOffset && (Txo.isInt4() || Txo.isInt8()))
Txo_int = Type::s32;
if (Txs == Type::f8_e8m0 && state.useBDPAS)
Txs_int = Type::f8_e8m0;
int r, c, k, rNoSLM, cNoSLM;
int tileR = 0, tileC = 0;
bool remR = false, remC = false;
if (isA) {
bool slmA = strategy.slmA;
rNoSLM = strategy.unroll[LoopM];
cNoSLM = strategy.ka_load;
r = slmA ? state.ma_slm : rNoSLM;
c = slmA ? state.ka_slm : cNoSLM;
k = slmA ? strategy.unrollKSLM : cNoSLM;
r = std::max(1, r / xqGroupMN);
c = state.kaq = std::max(1, c % xqGroupK == 0 ? c / xqGroupK : 1);
state.kaqStride = std::max(1, k % xqGroupK == 0 ? k / xqGroupK : 1);
rNoSLM = std::max(1, rNoSLM / xqGroupMN);
cNoSLM = state.kaqLate = std::max(1, cNoSLM % xqGroupK == 0 ? cNoSLM / xqGroupK: 1);
remR = (strategy.remHandling[LoopM] != RemainderHandling::Ignore);
if (xqGroupMN <= 1 && xqGroupK > 1) tileC = 1;
if (xqGroupMN > 1 && (xqGroupMN % strategy.unroll[LoopM] && strategy.unroll[LoopM] % xqGroupMN))
stub("Tile size not compatible with group size in m dimension");
} else {
bool slmB = strategy.slmB;
cNoSLM = strategy.unroll[LoopN];
rNoSLM = strategy.kb_load;
c = slmB ? state.nb_slm : cNoSLM;
r = slmB ? state.kb_slm : rNoSLM;
k = slmB ? strategy.unrollKSLM : rNoSLM;
c = std::max(1, c / xqGroupMN);
r = state.kbq = std::max(1, r % xqGroupK == 0 ? r / xqGroupK : 1);
state.kbqStride = std::max(1, k % xqGroupK == 0 ? k / xqGroupK : 1);
cNoSLM = std::max(1, cNoSLM / xqGroupMN);
rNoSLM = state.kbqLate = std::max(1, rNoSLM % xqGroupK == 0 ? rNoSLM / xqGroupK : 1);
remC = (strategy.remHandling[LoopN] != RemainderHandling::Ignore);
if (xqGroupMN <= 1 && xqGroupK > 1) tileR = 1;
if (xqGroupMN > 1 && (xqGroupMN % strategy.unroll[LoopN] && strategy.unroll[LoopN] % xqGroupMN))
stub("Tile size not compatible with group size in n dimension");
}
int ro = lateOffset ? rNoSLM : r;
int co = lateOffset ? cNoSLM : c;
int rs = lateScale ? rNoSLM : r;
int cs = lateScale ? cNoSLM : c;
if (X_strategy.padded) {
X_offsetStrategy.padded = X_scaleStrategy.padded = Xg_strategy.padded = true;
remR = remC = false;
}
bool wantCM = isA ^ (xqGroupMN > 1);
auto chooseAccess = [=](const MatrixAddressing &Xq) {
return (wantCM == isColMajor(Xq.layout)) ? AccessType::Block : AccessType::Scattered;
};
X_offsetStrategy.accessType = chooseAccess(XO);
X_scaleStrategy.accessType = chooseAccess(XS);
Xg_strategy.accessType = chooseAccess(Xg);
if (xo2D && !(X_offsetLayout = RegisterLayout::tryCreate(hw, Txo, ro, co, XO, X_offsetStrategy, remR, remC))) return false;
if (xs2D && !(X_scaleLayout = RegisterLayout::tryCreate(hw, Txs, rs, cs, XS, X_scaleStrategy, remR, remC))) return false;
if (xg2D && !(Xg_layout = RegisterLayout::tryCreate(hw, Txg, rNoSLM, cNoSLM, Xg, Xg_strategy, remR, remC))) return false;
auto adjustMask = [=](MaskInfo &mask) {
if (!mask || xqGroupMN <= 1) return;
if (!is_zero_or_pow2(xqGroupMN)) {
if (xqGroupMN < wgTileMN) stub();
return;
}
if (mask.fixed.isFixed) stub();
mask.variable.rshift += ilog2(xqGroupMN);
};
for (auto *Xq_layout: {&X_offsetLayout, &X_scaleLayout, &Xg_layout}) {
for (auto &block: *Xq_layout) {
adjustMask(block.rowMask);
adjustMask(block.colMask);
}
}
auto &lsrc = isA ? (strategy.slmA ? state.Ao_layout : !state.Ar_layout.empty() ? state.Ar_layout : state.A_layout)
: (strategy.slmB ? state.Bo_layout : !state.Br_layout.empty() ? state.Br_layout : state.B_layout);
if (lsrc.empty()) stub();
int crosspack = lsrc[0].crosspack;
if (xqGroupMN > 1)
crosspack = 1;
int cpo = lateOffset ? 1 : div_up(crosspack, cpoDiv);
int cps = lateScale ? 1 : crosspack;
auto makeQRepack = [&, tileR, tileC](Type Txq, Type Txq_int, RegisterLayout &repack, const RegisterLayout &src,
int m, int n, int cp, bool forceRepack) mutable {
if (cp > 1 || (cColMajor && (cp != src[0].crosspack)) || Txq != Txq_int || forceRepack) {
bool allowPartialRegs = false;
if (state.useBDPAS) {
allowPartialRegs = true;
cp = 1;
if (isA) {
tileR = problem.aqGroupK;
} else {
tileC = problem.bqGroupK;
}
}
repack = RegisterLayout(hw, Txq_int, m, n, wantCM, cp, tileR, tileC, allowPartialRegs);
}
};
if (xo2D) makeQRepack(Txo, Txo_int, Xr_offsetLayout, X_offsetLayout, ro, co, cpo, false);
if (xs2D) makeQRepack(Txs, Txs_int, Xr_scaleLayout, X_scaleLayout, rs, cs, cps, lateScale);
if (xg2D) makeQRepack(Txg, Txg_int, Xgr_layout, Xg_layout, rNoSLM, cNoSLM, 1, true);
if (xoTo2D) {
if (xoPtrDims <= 0)
Xr_offsetLayout = RegisterLayout(hw, Txo_int, 1, 1, isA);
else if (xoPtrDims == 1)
Xr_offsetLayout = RegisterLayout(hw, Txo_int, ro, co, isA, cpo, tileR, tileC, false);
else stub();
}
return true;
}
template <HW hw>
void Generator<hw>::gemmRepack2DQuantizationData(Type Ts, Type Td, const RegisterLayout &layoutSrc, const RegisterLayout &layoutDst,
const GRFMultirange &src, const GRFMultirange &dst,
const GEMMProblem &problem, const GEMMStrategy &strategy, GEMMState &state)
{
if (layoutDst.empty()) return;
for (int doffR = 0; doffR < layoutDst.rows(); doffR += layoutSrc.rows())
for (int doffC = 0; doffC < layoutDst.cols(); doffC += layoutSrc.cols())
copyRegisters(Ts, Td, layoutSrc, layoutDst, src, dst, doffR, doffC, false, strategy, state);
int r = layoutDst.rows();
int c = layoutDst.cols();
if(state.useBDPAS && r * c < minOuterProductCount(hw, problem, strategy)){
int halfRegElems = elementsPerGRF(hw, Td) / 2;
if(r * c > halfRegElems) stub();
RegisterLayout offsetLayout(layoutDst);
for( auto &b : offsetLayout){
if(b.nr * b.nc > halfRegElems) stub();
b.offsetBytes += (b.nr * b.nc);
}
copyRegisters(Td, Td, layoutDst, offsetLayout, dst, dst, 0, 0, false, strategy, state);
}
int cp = layoutDst[0].crosspack;
int p0 = layoutDst[0].colMajor ? layoutDst[0].nc : layoutDst[0].nr;
if (cp > 1) map(hw, Td, dst, layoutDst, strategy, [&](int simd, RegData r) {
Subregister r0 = GRF(r.getBase()).sub(r.getOffset(), r.getType());
moveToIntPipe(r0);
auto r1 = r0;
for (int i = p0; i < cp; i++) {
r1.setOffset(r1.getOffset() + 1);
mov(simd / cp, r1(cp), r0(cp));
}
});
}
template <HW hw>
void Generator<hw>::gemmRepack2DQuantizationData(const RegisterLayout &layoutSrc, const RegisterLayout &layoutDst,
const GRFMultirange &src, const GRFMultirange &dst,
const GEMMProblem &problem, const GEMMStrategy &strategy, GEMMState &state)
{
gemmRepack2DQuantizationData(layoutSrc.type(), layoutDst.type(), layoutSrc, layoutDst,
src, dst, problem, strategy, state);
}
template <HW hw>
void Generator<hw>::gemmRepack2DOffsetData(Type Text, const RegisterLayout &layoutSrc, const RegisterLayout &layoutDst,
const GRFMultirange &src, const GRFMultirange &dst,
const GEMMProblem &problem, const GEMMStrategy &strategy, GEMMState &state)
{
auto Ts = layoutSrc.type(), Td = layoutDst.type();
bool s4 = (Text == Type::s4);
bool s8 = (Ts == Type::s8);
bool u8 = (Ts == Type::u8);
bool int4SpecialPath = Text.isInt4() && Td == Type::f16;
auto tmpType = Td;
if (int4SpecialPath) {
if (u8) tmpType = Type::u16;
if (s8) tmpType = Type::s16;
}
gemmRepack2DQuantizationData(Ts, tmpType, layoutSrc, layoutDst, src, dst, problem, strategy, state);
if (int4SpecialPath) {
if (s8 || u8) {
int off = s4 ? 8 : 0;
if (s8) {
map(hw, Type::s16, dst, dst, strategy, [&](int esize, RegData r, RegData _) {
add(esize, r, r, 0x80);
});
off -= 0x80;
}
if (off != 0) map(hw, Type::f16, dst, dst, strategy, [&](int esize, RegData r, RegData _) {
uint16_t offF16 = std::abs(off);
if (off < 0) offF16 |= 0x8000;
add(esize, r, r, Immediate::hf(offF16));
});
map(hw, Type::f16, dst, dst, strategy, [&](int esize, RegData r, RegData _) {
mul(esize, r, r, Immediate::hf(0x6C00));
});
} else {
map(hw, Type::f16, dst, dst, strategy, [&](int esize, RegData r, RegData _) {
s4 ? mad(esize, r, Immediate::hf(0x1800), r, Immediate::hf(0x0C00)) : mul(esize, r, r, Immediate::hf(0x0C00)); });
}
}
}
template <HW hw>
void Generator<hw>::gemmDequantizeOperation(bool doA, Type T, Type Tq, BinaryOp op,
const RegisterLayout &layout, const RegisterLayout &qlayout,
const GRFMultirange ®s, const GRFMultirange &qregs,
int h, int kab_load, int kq_load, const GEMMProblem &problem, const CommonStrategy &strategy, CommonState &state)
{
int xqGroupK = doA ? problem.aqGroupK : problem.bqGroupK;
int xqGroupMN = doA ? problem.aqGroupM : problem.bqGroupN;
bool common = (qlayout.rows() * qlayout.cols()) == 1;
bool colMajor = layout.colMajor();
int xqGroupX = colMajor == doA ? xqGroupMN : xqGroupK;
int xqGroupY = colMajor == doA ? xqGroupK : xqGroupMN;
bool bfSpecialPath = (T == Type::bf16) && (Tq == Type::f32) && (xqGroupMN > 1) && layout.hasFullCrosspack(2) && (xqGroupY == 1);
GRFMultirange qPairs;
int npairs = 0;
if (bfSpecialPath) {
for (npairs = 8; npairs > 0; npairs >>= 1) {
qPairs = tryChunkAlloc(npairs, 1, Bundle(), BundleGroup::AllBundles(), state);
if (!qPairs.empty()) break;
}
if (qPairs.empty()) throw out_of_registers_exception();
}
for (auto &block: layout) {
auto crosspack = block.crosspack;
int nx = colMajor ? block.nr : block.nc;
int ny = colMajor ? block.nc : block.nr;
bool qbroadcastY = (xqGroupY % crosspack == 0) || common || bfSpecialPath;
int strided = 1;
if (!qbroadcastY) {
strided = crosspack;
}
bool qbroadcastX = xqGroupX > 1 || common;
int strideq = qbroadcastX ? 0 : 1;
if (bfSpecialPath)
strideq = 1;
auto h_block = align_down(h, kab_load) + (doA ? block.offsetC : block.offsetR);
auto mn_block = doA ? block.offsetR : block.offsetC;
for(int y0 = 0; y0 < ny; y0 += qbroadcastY ? crosspack : 1) {
for(int x0 = 0; x0 < nx; ) {
auto ii0 = colMajor ? x0 : y0;
auto jj0 = colMajor ? y0 : x0;
auto h_final = h_block + (doA ? jj0 : ii0);
auto mn_final = mn_block + (doA ? ii0 : jj0);
auto mnq = mn_final / xqGroupMN;
auto hq = (h_final % kq_load) / xqGroupK;
auto io0 = doA ? mnq : hq;
auto jo0 = doA ? hq : mnq;
if (common) io0 = jo0 = 0;
int ne, neq;
const RegisterBlock *qblock;
auto data = block.find(T, ii0, jj0, regs, &ne);
auto qdata = qlayout.find(io0, jo0, qregs, &neq, &qblock);
if (!qbroadcastX) ne = std::min(ne, neq);
if (xqGroupX > 1) ne = std::min(ne, xqGroupX - x0 % xqGroupX);
if (bfSpecialPath) {
int npair = (qdata.getOffset() / 2) % npairs;
if (x0 == 0 && npair == 0)
for (int p = 0; p < npairs; p++)
mov(elementsPerGRF(hw, Tq), qPairs[p].ud(), qdata.ud(2*p)(0,2,1));
qdata = qPairs[npair].sub(0, Tq.ngen());
}
int maxSIMD = (op == BinaryOp::Sub && T.isInt8()) ? 64 : 32;
if (Tq == Type::f32) maxSIMD = elementsPerGRF(hw, Tq);
int simd = std::min({ne * crosspack / strided, 2 * elementsPerGRF(hw, T) / strided, maxSIMD});
switch (op) {
case BinaryOp::Sub:
if (T.isInt8() && strided == 1) {
add(simd / 2, data(2), data(2), -qdata(strideq * 2 / Tq));
data.setOffset(data.getOffset() + 1);
qdata.setOffset(qdata.getOffset() + strideq / Tq);
add(simd / 2, data(2), data(2), -qdata(strideq * 2 / Tq));
} else
add(simd, data(strided), data(strided), -qdata(strideq));
break;
case BinaryOp::Mul:
emul(simd, data(strided), data(strided), qdata(strideq), strategy, state);
break;
case BinaryOp::ScaleSub:
if (T != Type::f16) stub();
mad(simd, data(strided), -qdata(strideq), data(strided), Immediate::hf(0x6C00));
break;
default: stub();
}
x0 += simd * strided / crosspack;
}
}
}
safeReleaseRanges(qPairs, state);
}
template <HW hw>
void Generator<hw>::dequantizeInt4Shift(Type Tsrc, GRFMultirange src, const CommonStrategy &strategy)
{
if (Tsrc != Type::s4) return;
map(hw, Type::u16, src, src, strategy, [&](int esize, RegData r, RegData _) {
xor_(esize, r, r, 0x8888);
});
}
template <HW hw>
void Generator<hw>::dequantizeInt4(bool doA, const RegisterLayout &layoutSrc, const RegisterLayout &layoutDst,
const RegisterLayout &layoutOffset, const RegisterLayout &layoutScale,
const GRFMultirange &src, const GRFMultirange &dst, const GRFMultirange &offset, const GRFMultirange &scale,
int offR, int offC, int h, int kab_load, int kq_load,
const GEMMProblem *problem, const CommonStrategy &strategy, CommonState &state, bool s4Shift)
{
auto Tsrc = layoutSrc.type(), Tdst = layoutDst.type();
if (!canDequantizeInt4(layoutSrc, layoutDst, layoutOffset, layoutScale))
stub("Cannot perform dequantizeInt4");
bool s4 = Tsrc.isSigned();
bool f32 = (Tdst == Type::f32);
bool bf16 = (Tdst == Type::bf16);
RegisterLayout layoutDstF16;
const RegisterLayout *effLayoutDst = &layoutDst;
GRFMultirange dstF16;
const GRFMultirange *effDst = &dst;
if (f32 || bf16) {
layoutDstF16 = RegisterLayout(hw, Type::f16, layoutSrc.rows(), layoutSrc.cols(), layoutDst.colMajor(), layoutDst.crosspack(), 0, 0, (hw < HW::XeHP));
for (auto &block: layoutDstF16) {
block.offsetR += layoutDst[0].offsetR;
block.offsetC += layoutDst[0].offsetC;
}
dstF16 = chunkAlloc(layoutDstF16.regs(), 2, state);
effLayoutDst = &layoutDstF16;
effDst = &dstF16;
}
if (s4 && s4Shift)
dequantizeInt4Shift(Tsrc, src, strategy);
copyRegisters(Type::u4, Type::u16, layoutSrc, *effLayoutDst, src, *effDst, offR, offC, false, strategy, state);
if (!layoutOffset.empty()) {
if (!problem) stub();
gemmDequantizeOperation(doA, Type::f16, Type::f16, BinaryOp::ScaleSub, *effLayoutDst, layoutOffset, *effDst, offset, h, kab_load, kq_load, *problem, strategy, state);
} else {
map(hw, Type::f16, *effDst, *effLayoutDst, strategy, [&](int esize, RegData r) {
s4 ? mad(esize, r, Immediate::hf(0x9800), r, Immediate::hf(0x6C00))
: mul(esize, r, r, Immediate::hf(0x6C00));
});
}
map(hw, Type::f16, *effDst, *effLayoutDst, strategy, [&](int esize, RegData r) {
mul(esize, r, r, Immediate::hf(0x6C00));
});
if (!f32 && !layoutScale.empty()) {
if (!problem) stub();
gemmDequantizeOperation(doA, Type::f16, Type::f16, BinaryOp::Mul, *effLayoutDst, layoutScale, *effDst, scale, h, kab_load, kq_load, *problem, strategy, state);
}
if (f32 || bf16) {
copyRegisters(Type::f16, Tdst, layoutDstF16, layoutDst, dstF16, dst, offR, offC, false, strategy, state);
safeReleaseRanges(dstF16, state);
}
if (f32 && !layoutScale.empty()) {
if (!problem) stub();
gemmDequantizeOperation(doA, Type::f32, Type::f32, BinaryOp::Mul, layoutDst, layoutScale, dst, scale, h, kab_load, kq_load, *problem, strategy, state);
}
}
template <HW hw>
void Generator<hw>::gemmDequantizeAB(bool doA, const RegisterLayout &layoutSrc, const RegisterLayout &layoutDst0,
const GRFMultirange &src, const GRFMultirange &dst0, int h, int kab_load, int kab_repack, int kq_load,
const GEMMProblem &problem, const GEMMStrategy &strategy, GEMMState &state,
bool s4Shift)
{
auto Tsrc = layoutSrc.type(), Tdst = layoutDst0.type();
auto Txo_int = doA ? state.Tao_int : state.Tbo_int;
auto Txs_int = doA ? state.Ta_scaleInt : state.Tb_scaleInt;
auto &oiLayout = doA ? state.A_offsetLayout : state.B_offsetLayout;
auto &orLayout = doA ? state.Ar_offsetLayout : state.Br_offsetLayout;
auto &oiRegs = doA ? state.A_offsetRegs : state.B_offsetRegs;
auto &orRegs = doA ? state.Ar_offsetRegs : state.Br_offsetRegs;
auto &siLayout = doA ? state.A_scaleLayout : state.B_scaleLayout;
auto &srLayout = doA ? state.Ar_scaleLayout : state.Br_scaleLayout;
auto &siRegs = doA ? state.A_scaleRegs : state.B_scaleRegs;
auto &srRegs = doA ? state.Ar_scaleRegs : state.Br_scaleRegs;
bool lateOffset = doA ? problem.needsBGroupSums() : problem.needsAGroupSums();
bool lateScale = doA ? state.lateScale2DA : state.lateScale2DB;
auto &oLayout = orLayout.empty() ? oiLayout : orLayout;
auto &oRegs = orLayout.empty() ? oiRegs : orRegs;
auto &sLayout = srLayout.empty() ? siLayout : srLayout;
auto &sRegs = srLayout.empty() ? siRegs : srRegs;
bool xo2D = !oLayout.empty() && !lateOffset;
bool xs2D = !sLayout.empty() && !lateScale;
bool copy = !layoutDst0.empty();
auto layoutDst = copy ? layoutDst0 : layoutSrc;
auto dst = copy ? dst0 : src;
auto Tx_int = xo2D ? Txo_int : Tdst;
if (xo2D && !xs2D && (Txo_int.bits() > Tdst.bits()))
Tx_int = Tdst;
int offR = doA ? 0 : h % kab_repack;
int offC = doA ? h % kab_repack : 0;
int offR0 = offR, offC0 = offC;
int ms = layoutSrc.rows(), ns = layoutSrc.cols();
int md = layoutDst.rows(), nd = layoutDst.cols();
if (ms < md || ns < nd) {
if (!copy) stub();
layoutDst = RegisterLayout(hw, Tdst, ms, ns, layoutDst0.colMajor(), layoutDst[0].crosspack);
dst = chunkAlloc(layoutDst.regs(), 2, state);
offR = offC = 0;
}
if (canDequantizeInt4(layoutSrc, layoutDst, oLayout, sLayout)) {
dequantizeInt4(doA, layoutSrc, layoutDst, oLayout, sLayout,
src, dst, oRegs, sRegs, offR, offC, h, kab_load, kq_load, &problem,
strategy, state, s4Shift);
} else {
if (copy)
copyRegisters(Tsrc, Tx_int, layoutSrc, layoutDst, src, dst, offR, offC, false, strategy, state);
else if (Tsrc.asSigned() != Tx_int.asSigned())
convert(src, Tsrc, Tx_int, strategy, state);
if (xo2D) {
if (!state.useBDPAS)
{
gemmDequantizeOperation(doA, Tx_int, Txo_int, BinaryOp::Sub, layoutDst, oLayout, dst, oRegs, h, kab_load, kq_load, problem, strategy, state);
convert(dst, Tx_int, Tdst, strategy, state);
}
}
if (xs2D)
if (!state.useBDPAS)
{
gemmDequantizeOperation(doA, Tdst, Txs_int, BinaryOp::Mul, layoutDst, sLayout, dst, sRegs, h, kab_load, kq_load, problem, strategy, state);
}
}
if (ms < md || ns < nd) {
copyRegisters(Tdst, Tdst, layoutDst, layoutDst0, dst, dst0, offR0, offC0, false, strategy, state);
safeReleaseRanges(dst, state);
}
}
GEMMSTONE_NAMESPACE_END