Skip to content

Commit 7bf54eb

Browse files
j2kuncopybara-github
authored andcommitted
preserve linalg.matvec as kernel.linear_transform in ConvertToCiphertextSemantics
This change adds a separate pattern PreserveLinalgMatvecAsLinearTransform with higher priority that identifies constant public matrix and secret vector inputs and preserves it as a generic kernel.linear_transform op. PiperOrigin-RevId: 961045685
1 parent 4ff76f5 commit 7bf54eb

4 files changed

Lines changed: 165 additions & 7 deletions

File tree

lib/Transforms/ConvertToCiphertextSemantics/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ cc_library(
1515
":TypeConversion",
1616
":pass_inc_gen",
1717
"@heir//lib/Dialect:ModuleAttributes",
18+
"@heir//lib/Dialect/Kernel/IR:Dialect",
1819
"@heir//lib/Dialect/Secret/IR:SecretAttributes",
1920
"@heir//lib/Dialect/Secret/IR:SecretPatterns",
2021
"@heir//lib/Dialect/TensorExt/IR:Dialect",
@@ -24,6 +25,7 @@ cc_library(
2425
"@heir//lib/Kernel:IRMaterializingVisitor",
2526
"@heir//lib/Kernel:KernelImplementation",
2627
"@heir//lib/Kernel:Utils",
28+
"@heir//lib/Target/CompilationTarget",
2729
"@heir//lib/Transforms/DropUnitDims",
2830
"@heir//lib/Transforms/LayoutPropagation:Utils",
2931
"@heir//lib/Utils",

lib/Transforms/ConvertToCiphertextSemantics/ConvertToCiphertextSemantics.cpp

Lines changed: 129 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <utility>
1313
#include <vector>
1414

15+
#include "lib/Dialect/Kernel/IR/KernelOps.h"
1516
#include "lib/Dialect/ModuleAttributes.h"
1617
#include "lib/Dialect/Secret/IR/SecretAttributes.h"
1718
#include "lib/Dialect/Secret/IR/SecretDialect.h"
@@ -26,6 +27,7 @@
2627
#include "lib/Kernel/KernelImplementation.h"
2728
#include "lib/Kernel/KernelName.h"
2829
#include "lib/Kernel/Utils.h"
30+
#include "lib/Target/CompilationTarget/CompilationTarget.h"
2931
#include "lib/Transforms/ConvertToCiphertextSemantics/AssignLayout.h"
3032
#include "lib/Transforms/ConvertToCiphertextSemantics/TypeConversion.h"
3133
#include "lib/Transforms/DropUnitDims/DropUnitDims.h"
@@ -895,6 +897,125 @@ struct ConvertLinalgMatvecLayout : public ConversionBase<linalg::MatvecOp> {
895897
bool unrollKernels;
896898
};
897899

900+
struct PreserveLinalgMatvecAsLinearTransform
901+
: public ConversionBase<linalg::MatvecOp> {
902+
public:
903+
using ConversionBase<linalg::MatvecOp>::ConversionBase;
904+
905+
PreserveLinalgMatvecAsLinearTransform(
906+
const ContextAwareTypeConverter& typeConverter, MLIRContext* context)
907+
: ConversionBase<linalg::MatvecOp>(typeConverter, context,
908+
/*benefit=*/20) {}
909+
910+
LogicalResult matchAndRewrite(
911+
linalg::MatvecOp op, OpAdaptor adaptor,
912+
ContextAwareConversionPatternRewriter& rewriter) const final {
913+
auto target = getTargetConfig(op->getParentOfType<ModuleOp>());
914+
if (failed(target) || !target->has_kernel_linear_transform) {
915+
return rewriter.notifyMatchFailure(op, "linear transform not enabled");
916+
}
917+
918+
Value matrixOperand = op.getInputs()[0];
919+
LayoutAttr matrixLayout = getLayoutAttr(matrixOperand);
920+
if (!matrixLayout) {
921+
return rewriter.notifyMatchFailure(op, "missing layout for matrix");
922+
}
923+
924+
Value matrix = matrixOperand;
925+
if (auto assignLayoutOp =
926+
matrix.getDefiningOp<tensor_ext::AssignLayoutOp>()) {
927+
matrix = assignLayoutOp.getValue();
928+
}
929+
auto constantMatrixOp = matrix.getDefiningOp<arith::ConstantOp>();
930+
if (!constantMatrixOp) {
931+
return rewriter.notifyMatchFailure(op, "matrix is not a constant");
932+
}
933+
auto denseAttr = dyn_cast<DenseElementsAttr>(constantMatrixOp.getValue());
934+
if (!denseAttr) {
935+
return rewriter.notifyMatchFailure(op,
936+
"matrix is not a DenseElementsAttr");
937+
}
938+
939+
auto matrixType = cast<RankedTensorType>(matrix.getType());
940+
auto convertedMatrixType = cast<ShapedType>(
941+
getTypeConverter()->convertType(matrixType, matrixLayout));
942+
if (!convertedMatrixType) {
943+
return rewriter.notifyMatchFailure(op, "failed to convert matrix type");
944+
}
945+
946+
int64_t numDiagonals = convertedMatrixType.getShape()[0];
947+
int64_t slots = convertedMatrixType.getShape()[1];
948+
auto elementType = matrixType.getElementType();
949+
950+
Attribute zeroAttr = rewriter.getZeroAttr(elementType);
951+
std::vector<Attribute> diagonalValues(numDiagonals * slots, zeroAttr);
952+
953+
auto matrixRelation = matrixLayout.getIntegerRelation();
954+
PointPairCollector collector(2, 2);
955+
enumeratePoints(matrixRelation, collector);
956+
957+
int64_t numCols = matrixType.getDimSize(1);
958+
for (const auto& pointPair : collector.points) {
959+
int64_t row = pointPair.first[0];
960+
int64_t col = pointPair.first[1];
961+
int64_t d = pointPair.second[0];
962+
int64_t s = pointPair.second[1];
963+
964+
int64_t flatIndex = row * numCols + col;
965+
Attribute val = denseAttr.getValues<Attribute>()[flatIndex];
966+
diagonalValues[d * slots + s] = val;
967+
}
968+
969+
std::vector<int64_t> nonZeroDiagonalIndices;
970+
std::vector<Attribute> nonZeroDiagonalValues;
971+
for (int64_t d = 0; d < numDiagonals; ++d) {
972+
bool isZero = true;
973+
for (int64_t s = 0; s < slots; ++s) {
974+
if (diagonalValues[d * slots + s] != zeroAttr) {
975+
isZero = false;
976+
break;
977+
}
978+
}
979+
if (!isZero) {
980+
nonZeroDiagonalIndices.push_back(d);
981+
for (int64_t s = 0; s < slots; ++s) {
982+
nonZeroDiagonalValues.push_back(diagonalValues[d * slots + s]);
983+
}
984+
}
985+
}
986+
987+
auto diagonalsType = RankedTensorType::get(
988+
{static_cast<int64_t>(nonZeroDiagonalIndices.size()), slots},
989+
elementType);
990+
auto diagonalsAttr =
991+
DenseElementsAttr::get(diagonalsType, nonZeroDiagonalValues);
992+
auto diagonalIndicesAttr =
993+
rewriter.getDenseI64ArrayAttr(nonZeroDiagonalIndices);
994+
995+
auto resultLayout = findAttributeAssociatedWith(
996+
op.getResult(0), tensor_ext::TensorExtDialect::kLayoutAttrName);
997+
if (failed(resultLayout)) {
998+
return rewriter.notifyMatchFailure(op, "missing output layout");
999+
}
1000+
1001+
auto outputType = op.getResult(0).getType();
1002+
auto convertedOutputType =
1003+
getTypeConverter()->convertType(outputType, resultLayout.value());
1004+
1005+
rewriter.setInsertionPointAfter(op);
1006+
auto linearTransformOp = rewriter.create<kernel::LinearTransformOp>(
1007+
op.getLoc(), convertedOutputType, adaptor.getInputs()[1], diagonalsAttr,
1008+
diagonalIndicesAttr, /*bsgs_ratio=*/nullptr);
1009+
1010+
setMaterializedAttr(linearTransformOp);
1011+
linearTransformOp->setAttr(kLayoutAttrName, resultLayout.value());
1012+
1013+
addBiasAndReplace(rewriter, op, linearTransformOp.getResult(),
1014+
adaptor.getOutputs()[0], resultLayout.value());
1015+
return success();
1016+
}
1017+
};
1018+
8981019
struct ConvertLinalgConv1D : public ConversionBase<linalg::Conv1DOp> {
8991020
public:
9001021
using ConversionBase<linalg::Conv1DOp>::ConversionBase;
@@ -2761,13 +2882,14 @@ struct ConvertToCiphertextSemantics
27612882
return isa<ModuleOp>(op) || hasMaterializedAttr(op);
27622883
});
27632884

2764-
patterns.add<ConvertAnyAddingMaterializedAttr, ConvertConvertLayout,
2765-
ConvertFunc, ConvertLinalgMatmul, ConvertLinalgBatchMatmul,
2766-
ConvertLinalgReduce, ConvertLinalgDot, ConvertSecretGeneric,
2767-
ConvertTensorCollapseShape, ConvertTensorExpandShape,
2768-
ConvertTensorExtractLayout, ConvertTensorExtractSlice,
2769-
ConvertTensorPad, ConvertTensorInsertLayout,
2770-
ConvertTensorInsertSlice>(typeConverter, context);
2885+
patterns.add<
2886+
ConvertAnyAddingMaterializedAttr, ConvertConvertLayout, ConvertFunc,
2887+
ConvertLinalgMatmul, ConvertLinalgBatchMatmul, ConvertLinalgReduce,
2888+
ConvertLinalgDot, ConvertSecretGeneric, ConvertTensorCollapseShape,
2889+
ConvertTensorExpandShape, ConvertTensorExtractLayout,
2890+
ConvertTensorExtractSlice, ConvertTensorPad, ConvertTensorInsertLayout,
2891+
ConvertTensorInsertSlice, PreserveLinalgMatvecAsLinearTransform>(
2892+
typeConverter, context);
27712893
patterns.add<ConvertLinalgMatvecLayout, ConvertLinalgConv1D,
27722894
ConvertLinalgConv2D, ConvertLinalgConv2DNchwFchw,
27732895
ConvertLinalgConv1DNcwFcw>(typeConverter, context,

lib/Transforms/ConvertToCiphertextSemantics/ConvertToCiphertextSemantics.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def ConvertToCiphertextSemantics : Pass<"convert-to-ciphertext-semantics"> {
4848
}];
4949
let dependentDialects = [
5050
"mlir::heir::tensor_ext::TensorExtDialect",
51+
"mlir::heir::kernel::KernelDialect",
5152
"mlir::linalg::LinalgDialect",
5253
"mlir::tensor::TensorDialect",
5354
"mlir::scf::SCFDialect"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: heir-opt %s --convert-to-ciphertext-semantics=min-slot-count=4 | FileCheck %s
2+
3+
// CHECK: module
4+
module attributes {
5+
backend.openfhe,
6+
backend.config_override = {has_kernel_linear_transform = true}
7+
} {
8+
// CHECK: @main
9+
func.func @main(%arg0: !secret.secret<tensor<4xf32>> {tensor_ext.layout = #tensor_ext.layout<"{ [i0] -> [ct, slot] : ct = 0 and slot = i0 and 0 <= i0 <= 3 and 0 <= slot <= 3 }">}) -> (!secret.secret<tensor<2xf32>> {tensor_ext.layout = #tensor_ext.layout<"{ [i0] -> [ct, slot] : ct = 0 and slot = i0 and 0 <= i0 <= 1 and 0 <= slot <= 3 }">}) {
10+
%cst = arith.constant dense<0.0> : tensor<2xf32>
11+
%cst_mat = arith.constant dense<[[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]]> : tensor<2x4xf32>
12+
%0 = tensor_ext.assign_layout %cst_mat {
13+
layout = #tensor_ext.layout<"{ [i0, i1] -> [ct, slot] : exists (e0: ct + slot - i1 - 4e0 = 0 and 0 <= ct <= 3) and slot - i0 = 0 and 0 <= i0 <= 1 and 0 <= i1 <= 3 and 0 <= slot <= 3 }">,
14+
tensor_ext.layout = #tensor_ext.layout<"{ [i0, i1] -> [ct, slot] : exists (e0: ct + slot - i1 - 4e0 = 0 and 0 <= ct <= 3) and slot - i0 = 0 and 0 <= i0 <= 1 and 0 <= i1 <= 3 and 0 <= slot <= 3 }">
15+
} : tensor<2x4xf32>
16+
%1 = tensor_ext.assign_layout %cst {
17+
layout = #tensor_ext.layout<"{ [i0] -> [ct, slot] : ct = 0 and slot = i0 and 0 <= i0 <= 1 and 0 <= slot <= 3 }">,
18+
tensor_ext.layout = #tensor_ext.layout<"{ [i0] -> [ct, slot] : ct = 0 and slot = i0 and 0 <= i0 <= 1 and 0 <= slot <= 3 }">
19+
} : tensor<2xf32>
20+
%2 = secret.generic(%arg0 : !secret.secret<tensor<4xf32>> {tensor_ext.layout = #tensor_ext.layout<"{ [i0] -> [ct, slot] : ct = 0 and slot = i0 and 0 <= i0 <= 3 and 0 <= slot <= 3 }">}) {
21+
^body(%input: tensor<4xf32>):
22+
// CHECK: kernel.linear_transform
23+
// CHECK-SAME: diagonal_indices = array<i64: 0, 1, 2, 3>
24+
// CHECK-SAME: diagonals = dense<{{\[\[}}1.000000e+00, 6.000000e+00, 0.000000e+00, 0.000000e+00], [2.000000e+00, 7.000000e+00, 0.000000e+00, 0.000000e+00], [3.000000e+00, 8.000000e+00, 0.000000e+00, 0.000000e+00], [4.000000e+00, 5.000000e+00, 0.000000e+00, 0.000000e+00]]> : tensor<4x4xf32>
25+
%3 = linalg.matvec {
26+
secret.kernel = #secret.kernel<name = "MatvecDiagonal", force = false>,
27+
tensor_ext.layout = #tensor_ext.layout<"{ [i0] -> [ct, slot] : ct = 0 and slot = i0 and 0 <= i0 <= 1 and 0 <= slot <= 3 }">
28+
} ins(%0, %input : tensor<2x4xf32>, tensor<4xf32>) outs(%1 : tensor<2xf32>) -> tensor<2xf32>
29+
secret.yield %3 : tensor<2xf32>
30+
} -> (!secret.secret<tensor<2xf32>> {tensor_ext.layout = #tensor_ext.layout<"{ [i0] -> [ct, slot] : ct = 0 and slot = i0 and 0 <= i0 <= 1 and 0 <= slot <= 3 }">})
31+
return %2 : !secret.secret<tensor<2xf32>>
32+
}
33+
}

0 commit comments

Comments
 (0)