Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1b01f9c
perf(PULPOpen): let Gemm take a one-dimensional bias instead of widen…
runwangdl Jul 28, 2026
a993417
perf(PULPOpen): add a vector bias without materialising the broadcast
runwangdl Jul 28, 2026
d25ccae
feat(PULPOpen): matmul that dequantises an int8 weight in-kernel
runwangdl Jul 28, 2026
a188539
feat(PULPOpen): fold a weight's Dequant into the matmul as a topology…
runwangdl Jul 28, 2026
36598fb
test(gap9): add the LoRA and QLoRA CCT training models to CI
runwangdl Jul 28, 2026
f11b1c6
fix(PULPOpen): transfer only the bias vector for a broadcast Gemm bias
runwangdl Jul 28, 2026
77db3dd
Merge remote-tracking branch 'origin/devel' into fix/gemm50
runwangdl Jul 28, 2026
93bd906
Merge remote-tracking branch 'origin/feat/gemm-vector-bias' into fix/…
runwangdl Jul 28, 2026
e1ad232
Merge remote-tracking branch 'origin/devel' into fix/combo54
runwangdl Jul 28, 2026
84139b4
Merge branch 'fix/add51' into fix/combo54
runwangdl Jul 28, 2026
7a1831a
feat(GAP9): register the fused dequant kernels on GAP9 and the backwa…
runwangdl Jul 28, 2026
e8720bc
Merge remote-tracking branch 'origin/feat/fused-dequant-gemm' into fi…
runwangdl Jul 28, 2026
4c2658c
Merge commit '7a1831a5' into fix/combo54
runwangdl Jul 28, 2026
d16d0b5
fix(frontend): duplicate constants again after folding
runwangdl Jul 28, 2026
189e0f5
fix(bindings): match the fp32 kernel before the folded-int8 one
runwangdl Jul 29, 2026
57fb1f4
feat(training): replay gradient-checkpointing schedules, and run the …
runwangdl Jul 29, 2026
1a27afa
fix(PULPOpen): select the folded-int8 kernel by the fold's own marker
runwangdl Jul 29, 2026
8862c7f
fix(GAP9): declare the MatMul kernels GAP9 emits
runwangdl Jul 29, 2026
10c80cd
fix(PULPOpen): size and orient a broadcast Add by the output
runwangdl Jul 29, 2026
3840229
fix(test): commit the recompute schedule the CI job names
runwangdl Jul 29, 2026
ddffedf
fix(pre-commit): keep clang-format off JSON
runwangdl Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/ci-platform-gap9.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,19 @@ jobs:
with:
runner: ${{ needs.select-env.outputs.runner }}
docker-image: ${{ needs.select-env.outputs.image }}
pytest-markers: "gap9_tiled and training and singlebuffer and l3"
pytest-markers: "gap9_tiled and training and singlebuffer and l3 and not recompute"

# Gradient-checkpointed training: the same models and budgets as the singlebuffer
# job, but replaying a solved recompute schedule so checkpointed activations are
# regenerated before the backward pass rather than held live. Its own job so it
# does not stretch the singlebuffer job's runtime and a failure is attributable.
gap9-training-tiled-l3-recompute:
needs: select-env
uses: ./.github/workflows/_runner-gap9-tiled.yml
with:
runner: ${{ needs.select-env.outputs.runner }}
docker-image: ${{ needs.select-env.outputs.image }}
pytest-markers: "gap9_tiled and training and l3 and recompute"

# L3 double-buffered training (CCT) — promote+DB handled by its own job below
gap9-training-tiled-l3-doublebuffer:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ DeeployTest/Tests/**/pactIntegerizationProto
DeeployTest/Tests/**/quantlib
DeeployTest/Tests/**/*.py
DeeployTest/Tests/**/*.json
# Recompute schedules are inputs, not generated artefacts: a test that names one
# and cannot find it fails at generation, which is how it reached CI unnoticed.
!DeeployTest/Tests/**/recompute_*.json
DeeployTest/Tests/**/generateTest.py
DeeployTest/out.txt

Expand Down
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ repos:
- id: clang-format
name: Autoformat C/C++ Files
args: ["-i"]
# The hook's default file set includes .json, and reformatting a recompute
# schedule leaves it unparseable. Only C and C++ here.
types_or: [c, c++]
stages: [pre-commit, pre-merge-commit, pre-push, manual]
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.33.0
Expand Down
17 changes: 15 additions & 2 deletions Deeploy/DeeployTypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -979,8 +979,12 @@ def hoistConstant(self,
Returns the name of the newly registed ConstantBuffer

"""
assert len(constant.outputs) <= 1, f"Constant {constant.name} has more than one output"

# A ConstantBuffer is global and read-only, so several nodes reading one is
# sound and needs no copy. Requiring a single consumer blocked every graph
# where a frozen weight is read twice: a quantised weight reaching both the
# forward MatMul and its backward Gemm, and a transposed weight shared the
# same way after constant folding. Duplicating to satisfy the assertion is
# not free either, since it charges the full constant again per consumer.
name = name if name is not None else constant.name

# LMACAN: The shape needs to be copied into a tuple for pickling to work. Don't ask me why..
Expand Down Expand Up @@ -3467,6 +3471,15 @@ def frontEnd(self):
log.debug(" - Constant Folding")
self._foldConstants(self.graph)

# Folding can hand a single constant to several consumers: a weight reached
# through Constant -> Transpose -> Variable collapses to one Constant, and a
# training graph has the forward MatMul and the backward Gemm both reading it.
# Duplication ran before the fold, so nothing splits that, and hoistConstant
# then asserts. Run it again; it only touches tensors with more than one
# consumer, so it is a no-op when the fold introduced none.
log.debug(" - Duplicate Constants (post-fold)")
self._duplicateConstants(self.graph)

log.info(f"> Export State to {_middlewarePreLoweringFilename}[.onnx|.pkl]")
self.exportDeeployState(self.deeployStateDir, _middlewarePreLoweringFilename)

Expand Down
47 changes: 42 additions & 5 deletions Deeploy/Targets/GAP9/Bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@
from Deeploy.Targets.PULPOpen.Templates import ConvTemplate, DMASliceTemplate, FloatAddTemplate, \
FloatAveragePoolTemplate, FloatBatchNormTemplate, FloatConvGradTemplate, FloatConvTemplate, FloatGELUTemplate, \
FloatGemmTemplate, FloatGlobalAveragePoolTemplate, FloatInPlaceAccumulatorV2Template, FloatLayernormTemplate, \
FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatReluTemplate, FloatSoftmaxTemplate, \
GEMMTemplate, MatrixVectorTemplate, MaxPoolTemplate, MSELossTemplate, MulTemplate, ReduceMeanTemplate, \
RequantShiftTemplate, ReshapeTemplate, RQAddTemplate, RQSiHardswishTemplate, SGDTemplate, \
FloatMatMulDequantTemplate, FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatReluTemplate, \
FloatSoftmaxTemplate, GEMMTemplate, MatrixVectorTemplate, MaxPoolTemplate, MSELossTemplate, MulTemplate, \
ReduceMeanTemplate, RequantShiftTemplate, ReshapeTemplate, RQAddTemplate, RQSiHardswishTemplate, SGDTemplate, \
SoftmaxCrossEntropyLossTemplate, TallGEMMTemplate, TransposeTemplate, UniformRequantShiftTemplate, \
iRMSNormTemplate, iSoftmaxTemplate
from Deeploy.Targets.PULPOpen.TypeCheckers import PULPConvChecker, PULPLinearChecker, PULPMaxPoolChecker, \
PULPRequantShiftChecker
from Deeploy.Targets.PULPOpen.TypeCheckers import PULPConvChecker, PULPDequantConvChecker, PULPDequantGEMMChecker, \
PULPDequantMatMulChecker, PULPLinearChecker, PULPMaxPoolChecker, PULPRequantShiftChecker
from Deeploy.TilingExtension.CodeTransformationPasses.TilingVariableReplacement import TilingVariableReplacement, \
TilingVariableReplacementUpdate

Expand Down Expand Up @@ -157,6 +157,12 @@
AddTemplate.referenceTemplate, GAP9Transformer)
for type1 in IntegerDataTypes
for type2 in IntegerDataTypes
] + [
# fp32 activations against an int8 weight whose Dequant has been folded in, so
# the dequantised weight is never materialised. GAP9 keeps its own binding list;
# adding this to PULPMatMulBindings alone has no effect here.
NodeBinding(PULPDequantMatMulChecker([PointerClass(float32_t), PointerClass(int8_t)], [PointerClass(float32_t)]),
FloatMatMulDequantTemplate.referenceTemplate, GAP9Transformer)
] + [
NodeBinding(AddChecker([PointerClass(float32_t), PointerClass(float32_t)], [PointerClass(float32_t)]),
FloatAddTemplate.referenceTemplate, GAP9Transformer)
Expand Down Expand Up @@ -196,13 +202,37 @@
]

GAP9FloatGEMMBindings = [
# int8 weight with the Dequant folded in. Needed on the BACKWARD Gemm too, not
# just the forward MatMul: they share the weight constant, and if the backward
# one selects an fp32 binding, typeInferGlobalCtxt re-types the shared constant
# to float and its allocation quadruples.
NodeBinding(
PULPDequantGEMMChecker(
[PointerClass(float32_t), PointerClass(int8_t),
PointerClass(float32_t)], [PointerClass(float32_t)]), FloatGemmTemplate.referenceDequantTemplate,
GAP9Transformer),
# fp32 first. A binding is matched before a weight constant's type is fixed, and
# typeInferGlobalCtxt then types that constant from the SELECTED binding's
# input_types. With the int8 variant first, an ordinary fp32 weight is claimed by
# it and retyped to int8, silently quantising it: the FP32 Conv kernel test
# failed 512 of 512 and the emitted GAP9 call had no visible declaration. A
# genuinely int8 constant already carries that type, so the fp32 checker rejects
# it and the int8 binding below still wins.
NodeBinding(
GEMMChecker([PointerClass(float32_t), PointerClass(float32_t),
PointerClass(float32_t)], [PointerClass(float32_t)]), FloatGemmTemplate.referenceTemplate,
GAP9Transformer)
]

GAP9FloatConv2DBindings = [
# int8 weight with the Dequant folded in. GAP9 keeps its own binding list, so
# this has to be added here and not only in PULPFloatConv2DBindings.
NodeBinding(
PULPDequantConvChecker(
[PointerClass(float32_t), PointerClass(int8_t),
PointerClass(float32_t)], [PointerClass(float32_t)]), FloatConvTemplate.reference2DIm2ColDequantTemplate,
GAP9Transformer),
# fp32 first, for the reason given on GAP9FloatGEMMBindings above.
NodeBinding(
ConvChecker([PointerClass(float32_t), PointerClass(float32_t),
PointerClass(float32_t)], [PointerClass(float32_t)]), FloatConvTemplate.reference2DIm2ColTemplate,
Expand Down Expand Up @@ -280,6 +310,13 @@
GAP9MatMulBindings = [
NodeBinding(MatMulChecker([PointerClass(int8_t), PointerClass(int8_t)], [PointerClass(int32_t)]),
GEMMTemplate.PULPMM_8_Template, GAP9ClusterTransformer)
] + [
# int8 weight with the Dequant folded in. PULPDequantMatMulChecker matches only a
# node the fold marked with dequant_scale, so this must come first to be reached
# at all (binding selection stops at the first type check that passes) while
# staying invisible to an ordinary fp32 graph.
NodeBinding(PULPDequantMatMulChecker([PointerClass(float32_t), PointerClass(int8_t)], [PointerClass(float32_t)]),
FloatMatMulDequantTemplate.referenceTemplate, GAP9Transformer)
] + [
NodeBinding(MatMulChecker([PointerClass(float32_t), PointerClass(float32_t)], [PointerClass(float32_t)]),
FloatMatMulTemplate.referenceTemplate, GAP9Transformer)
Expand Down
17 changes: 8 additions & 9 deletions Deeploy/Targets/GAP9/Platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@
BasicRQIntegerDivBinding
from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolGradLayer, BatchNormalizationGradLayer, \
BatchNormInternalLayer, ConcatLayer, ConvGradBLayer, ConvGradWLayer, ConvGradXLayer, ConvLayer, GatherLayer, \
GELUGradLayer, GELULayer, GEMMLayer, GlobalAveragePoolGradLayer, GlobalAveragePoolLayer, \
InPlaceAccumulatorV2Layer, LayerNormGradLayer, LayerNormLayer, MatMulLayer, MaxPoolLayer, MSELossGradLayer, \
MSELossLayer, MulLayer, PadLayer, QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluGradLayer, ReluLayer, \
RequantShiftLayer, ReshapeLayer, RQIntegerDivLayer, RQSiGELULayer, RQSiHardswishLayer, SGDLayer, SliceLayer, \
SoftmaxCrossEntropyLossGradLayer, SoftmaxCrossEntropyLossLayer, SoftmaxGradLayer, SoftmaxLayer, TransposeLayer, \
iHardswishLayer, iRMSNormLayer
GELUGradLayer, GELULayer, GlobalAveragePoolGradLayer, GlobalAveragePoolLayer, InPlaceAccumulatorV2Layer, \
LayerNormGradLayer, LayerNormLayer, MatMulLayer, MaxPoolLayer, MSELossGradLayer, MSELossLayer, MulLayer, PadLayer, \
QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluGradLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, \
RQIntegerDivLayer, RQSiGELULayer, RQSiHardswishLayer, SGDLayer, SliceLayer, SoftmaxCrossEntropyLossGradLayer, \
SoftmaxCrossEntropyLossLayer, SoftmaxGradLayer, SoftmaxLayer, TransposeLayer, iHardswishLayer, iRMSNormLayer
from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool2DParser, BatchNormalizationGradParser, \
BatchNormInternalParser, ConcatParser, Conv2DGradBParser, DequantParser, FlattenParser, GatherParser, \
GELUGradParser, GELUParser, GEMMParser, GlobalAveragePoolGradParser, GlobalAveragePoolParser, \
Expand All @@ -52,7 +51,7 @@
from Deeploy.Targets.Generic.Templates import AllocateTemplate as BasicAllocateTemplate
from Deeploy.Targets.PULPOpen.Bindings import BasicQuantBindings, PULPDMASliceBindings, PULPDWConv1DBinding, \
PULPReduceMeanBindings, PULPRQSConv1DBindings
from Deeploy.Targets.PULPOpen.Layers import PULPRQSConvLayer, PULPRQSGEMMLayer
from Deeploy.Targets.PULPOpen.Layers import PULPAddLayer, PULPGEMMLayer, PULPRQSConvLayer, PULPRQSGEMMLayer
from Deeploy.Targets.PULPOpen.Parsers import PULPConv1DParser, PULPConv2DParser, PULPConvGradW2DParser, \
PULPConvGradX2DParser, PULPDWConv1DParser, PULPDWConv2DParser, PULPDWConvGradW2DParser, PULPDWConvGradX2DParser, \
PULPFPConv2DCHWParser, PULPFPConv2DParser, PULPFPDWConv2DCHWParser, PULPFPDWConv2DParser, PULPGEMMParser, \
Expand Down Expand Up @@ -145,7 +144,7 @@
'RequantizedGemm':
PULPRQSGEMMLayer([GAP9_MatrixVecMapper, GAP9_TallGEMMMapper, GAP9_GEMMMapper]),
'Gemm':
GEMMLayer([GAP9_FloatGEMMMapper, GAP9_GEMMDequantMapper]),
PULPGEMMLayer([GAP9_FloatGEMMMapper, GAP9_GEMMDequantMapper]),
'Gelu':
GELULayer([GAP9_GELUMapper]),
'GeluGrad':
Expand Down Expand Up @@ -173,7 +172,7 @@
'RequantShift':
RequantShiftLayer([GAP9_UniformRequantShiftMapper, GAP9_RequantShiftMapper]),
'Add':
AddLayer([GAP9_AddMapper]),
PULPAddLayer([GAP9_AddMapper]),
'Flatten':
ReshapeLayer([GAP9_FlattenMapper]),
'Gather':
Expand Down
3 changes: 2 additions & 1 deletion Deeploy/Targets/GAP9/Tiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from Deeploy.Targets.PULPOpen.TileConstraints.AveragePoolTileConstraint import AveragePoolCTileConstraint
from Deeploy.Targets.PULPOpen.TileConstraints.BatchNormTileConstraint import BatchNormalizationGradTileConstraint, \
BatchNormInternalTileConstraint
from Deeploy.Targets.PULPOpen.TileConstraints.BroadcastAddTileConstraint import PULPBroadcastAddTileConstraint
from Deeploy.Targets.PULPOpen.TileConstraints.ConvGradConstraint import ConvGradBTileConstraint, \
ConvGradW2DTileConstraint, ConvGradX2DIm2ColHWTileConstraint, DWConvGradW2DTileConstraint, \
DWConvGradX2DTileConstraint, PWConvGradWTileConstraint, PWConvGradXTileConstraint
Expand Down Expand Up @@ -130,7 +131,7 @@
tileConstraint = TransposeTileConstraint())

GAP9AddTilingReadyBindings = TilingReadyNodeBindings(nodeBindings = GAP9AddBindings,
tileConstraint = AddTileConstraint())
tileConstraint = PULPBroadcastAddTileConstraint())

GAP9SoftmaxTilingReadyBindings = TilingReadyNodeBindings(nodeBindings = GAP9SoftmaxBindings,
tileConstraint = iSoftmaxTileConstraint())
Expand Down
15 changes: 14 additions & 1 deletion Deeploy/Targets/Generic/Parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,13 @@ def parseNodeCtxt(self,
self.operatorRepresentation['data_in_1'] = data_in_1.name
self.operatorRepresentation['data_in_2'] = data_in_2.name
self.operatorRepresentation['data_out'] = data_out.name
self.operatorRepresentation['size'] = np.prod(data_in_1.shape)
# Size the loop by the output, not by an input. With both operands the same
# shape these agree, but a broadcast operand does not: PULPAddLayer leaves a
# Linear's [O] bias at its own shape rather than storing the same row per
# output row, and taking the first input's size then bounded the loop at the
# bias length. The remaining outputs were never written and read back as
# whatever was in memory.
self.operatorRepresentation['size'] = np.prod(data_out.shape)

return ctxt, True

Expand Down Expand Up @@ -2064,6 +2070,13 @@ def parseNode(self, node: gs.Node) -> (bool):
self.operatorRepresentation['transB'] = 0
self.operatorRepresentation['transA'] = 0

# A weight-only-quantised graph can fold its Dequant into this node, in
# which case the B operand arrives as int8 and carries the per-tensor
# affine parameters the kernel needs. Defaults keep the fp32 path
# unchanged for every graph that has no folded Dequant.
self.operatorRepresentation['dequant_scale'] = float(node.attrs.get('dequant_scale', 1.0))
self.operatorRepresentation['dequant_zero_point'] = int(node.attrs.get('dequant_zero_point', 0))

return ret

def parseNodeCtxt(self,
Expand Down
40 changes: 34 additions & 6 deletions Deeploy/Targets/PULPOpen/Bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@
from Deeploy.Targets.PULPOpen.Templates import ConvTemplate, DMASliceTemplate, FloatAddTemplate, \
FloatAveragePoolTemplate, FloatBatchNormTemplate, FloatConvGradTemplate, FloatConvTemplate, FloatGELUTemplate, \
FloatGemmTemplate, FloatGlobalAveragePoolTemplate, FloatInPlaceAccumulatorV2Template, FloatLayernormTemplate, \
FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatReduceMeanTemplate, FloatReluTemplate, \
FloatSoftmaxTemplate, GEMMTemplate, MatrixVectorTemplate, MaxPoolTemplate, MSELossTemplate, MulTemplate, \
ReduceMeanTemplate, RequantShiftTemplate, ReshapeTemplate, RQAddTemplate, RQSiHardswishTemplate, SGDTemplate, \
SoftmaxCrossEntropyLossTemplate, TallGEMMTemplate, TransposeTemplate, UniformRequantShiftTemplate, \
FloatMatMulDequantTemplate, FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatReduceMeanTemplate, \
FloatReluTemplate, FloatSoftmaxTemplate, GEMMTemplate, MatrixVectorTemplate, MaxPoolTemplate, MSELossTemplate, \
MulTemplate, ReduceMeanTemplate, RequantShiftTemplate, ReshapeTemplate, RQAddTemplate, RQSiHardswishTemplate, \
SGDTemplate, SoftmaxCrossEntropyLossTemplate, TallGEMMTemplate, TransposeTemplate, UniformRequantShiftTemplate, \
iRMSNormTemplate, iSoftmaxTemplate
from Deeploy.Targets.PULPOpen.TypeCheckers import PULPConvChecker, PULPLinearChecker, PULPMaxPoolChecker, \
PULPRequantShiftChecker
from Deeploy.Targets.PULPOpen.TypeCheckers import PULPConvChecker, PULPDequantConvChecker, PULPDequantMatMulChecker, \
PULPLinearChecker, PULPMaxPoolChecker, PULPRequantShiftChecker
from Deeploy.TilingExtension.CodeTransformationPasses.TilingVariableReplacement import TilingVariableReplacement, \
TilingVariableReplacementUpdate

Expand Down Expand Up @@ -193,6 +193,11 @@
AddTemplate.referenceTemplate, ForkTransformer)
for type1 in IntegerDataTypes
for type2 in IntegerDataTypes
] + [
# fp32 activations against an int8 weight: the Dequant has been folded into the
# matmul, so the weight is never materialised as fp32.
NodeBinding(PULPDequantMatMulChecker([PointerClass(float32_t), PointerClass(int8_t)], [PointerClass(float32_t)]),
FloatMatMulDequantTemplate.referenceTemplate, ForkTransformer)
] + [
NodeBinding(AddChecker([PointerClass(float32_t), PointerClass(float32_t)], [PointerClass(float32_t)]),
FloatAddTemplate.referenceTemplate, ForkTransformer)
Expand Down Expand Up @@ -239,6 +244,18 @@
]

PULPFloatConv2DBindings = [
NodeBinding(
PULPDequantConvChecker(
[PointerClass(float32_t), PointerClass(int8_t),
PointerClass(float32_t)], [PointerClass(float32_t)]), FloatConvTemplate.reference2DIm2ColDequantTemplate,
ForkTransformer),
# fp32 first. A binding is matched before a weight constant's type is fixed, and
# typeInferGlobalCtxt then types that constant from the SELECTED binding's
# input_types. Listing the int8 variant first does not make it "more specific":
# it claims an ordinary fp32 weight and retypes it to int8, silently quantising
# it. That is what failed the FP32 Conv kernel test 512 of 512. A genuinely int8
# constant already carries that type, so the fp32 checker rejects it and the
# int8 binding below still wins.
NodeBinding(
ConvChecker([PointerClass(float32_t), PointerClass(float32_t),
PointerClass(float32_t)], [PointerClass(float32_t)]), FloatConvTemplate.reference2DIm2ColTemplate,
Expand Down Expand Up @@ -390,6 +407,17 @@
PULPMatMulBindings = [
NodeBinding(MatMulChecker([PointerClass(int8_t), PointerClass(int8_t)], [PointerClass(int32_t)]),
GEMMTemplate.PULPMM_8_Template, ClusterTransformer)
] + [
# int8 weight with the Dequant folded in. Ordering alone cannot separate this from
# the fp32 binding: selection happens before a weight constant's type is fixed and
# typeInferGlobalCtxt then types that constant from the SELECTED binding, so
# whichever comes first claims the weight and defines its type. int8-first
# silently quantised ordinary fp32 weights (FP32 Conv kernel test, 512 of 512);
# fp32-first inflated folded int8 weights (CCT-QLoRA persistent 409 KB -> 1094 KB).
# PULPDequantMatMulChecker keys on the dequant_scale attribute the fold leaves
# behind instead, so it is invisible to an fp32 graph and can safely come first.
NodeBinding(PULPDequantMatMulChecker([PointerClass(float32_t), PointerClass(int8_t)], [PointerClass(float32_t)]),
FloatMatMulDequantTemplate.referenceTemplate, ForkTransformer)
] + [
NodeBinding(MatMulChecker([PointerClass(float32_t), PointerClass(float32_t)], [PointerClass(float32_t)]),
FloatMatMulTemplate.referenceTemplate, ForkTransformer)
Expand Down
Loading
Loading