Skip to content

Commit 2878ccc

Browse files
Merge pull request #2818 from AlexanderViand:analysis-interfaces
PiperOrigin-RevId: 891704982
2 parents e003cbb + 938c027 commit 2878ccc

25 files changed

Lines changed: 320 additions & 125 deletions

lib/Analysis/RotationAnalysis/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ cc_library(
5252
"@heir//lib/Kernel:AbstractValue",
5353
"@heir//lib/Kernel:ArithmeticDag",
5454
"@heir//lib/Kernel:EvalVisitor",
55+
"@heir//lib/Utils:RotationUtils",
5556
"@llvm-project//llvm:Support",
5657
],
5758
)

lib/Analysis/RotationAnalysis/DagBuilder.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,17 @@ FailureOr<NodePtr> DagBuilder::visit(scf::YieldOp op) {
170170

171171
FailureOr<NodePtr> DagBuilder::visit(RotationOpInterface op) {
172172
LDBG() << "Processing RotationOpInterface " << op;
173-
OpFoldResult ofr = op.getRotationIndex();
174-
NodePtr shift;
175-
if (auto attr = dyn_cast<Attribute>(ofr)) {
176-
auto intAttr = cast<IntegerAttr>(attr);
177-
shift = Node::constantScalar(intAttr.getInt(),
178-
mlirTypeToDagType(intAttr.getType()));
179-
} else {
180-
shift = findNodeOrMakeNewVariable(cast<Value>(ofr));
181-
}
173+
auto indices = op.getRotationIndices();
174+
auto toShiftNode = [&](OpFoldResult ofr) -> NodePtr {
175+
if (auto attr = dyn_cast<Attribute>(ofr)) {
176+
auto intAttr = cast<IntegerAttr>(attr);
177+
return Node::constantScalar(intAttr.getInt(),
178+
mlirTypeToDagType(intAttr.getType()));
179+
}
180+
return findNodeOrMakeNewVariable(cast<Value>(ofr));
181+
};
182182

183183
// Find the rotatable operand.
184-
// We assume it's the operand that has the same type as the result.
185184
OpOperand* rotatedOperand = op.getRotatedOperand();
186185

187186
if (!rotatedOperand) {
@@ -192,7 +191,18 @@ FailureOr<NodePtr> DagBuilder::visit(RotationOpInterface op) {
192191
}
193192

194193
auto tensorNode = findNodeOrMakeNewVariable(rotatedOperand->get());
195-
auto dagNode = Node::leftRotate(tensorNode, shift);
194+
195+
NodePtr dagNode;
196+
if (indices.size() == 1) {
197+
dagNode = Node::leftRotate(tensorNode, toShiftNode(indices[0]));
198+
} else {
199+
std::vector<NodePtr> shifts;
200+
shifts.reserve(indices.size());
201+
for (auto& ofr : indices) {
202+
shifts.push_back(toShiftNode(ofr));
203+
}
204+
dagNode = Node::leftRotateBulk(tensorNode, std::move(shifts));
205+
}
196206
valueToNode[op->getResult(0)] = dagNode;
197207
return dagNode;
198208
}

lib/Analysis/RotationAnalysis/RotationAnalysis.cpp

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,28 @@ LogicalResult RotationAnalysis::analyzeRotationOp(
6565

6666
// Handle cases where the rotation shift can be statically folded via constant
6767
// propagation.
68-
OpFoldResult ofr = rotationOp.getRotationIndex();
69-
if (auto attr = dyn_cast_if_present<Attribute>(ofr)) {
70-
if (auto intAttr = dyn_cast<IntegerAttr>(attr)) {
71-
rotationIndices.insert(intAttr.getInt());
72-
markVisited(rotationOp);
73-
return success();
68+
auto indices = rotationOp.getRotationIndices();
69+
SmallVector<int64_t> constantIndices;
70+
bool allConstant = true;
71+
for (auto& ofr : indices) {
72+
if (auto attr = dyn_cast_if_present<Attribute>(ofr)) {
73+
if (auto intAttr = dyn_cast<IntegerAttr>(attr)) {
74+
constantIndices.push_back(intAttr.getInt());
75+
continue;
76+
}
7477
}
78+
if (auto value = dyn_cast<Value>(ofr)) {
79+
IntegerAttr attr;
80+
if (matchPattern(value, m_Constant(&attr))) {
81+
constantIndices.push_back(attr.getInt());
82+
continue;
83+
}
84+
}
85+
allConstant = false;
86+
break;
7587
}
76-
77-
Value value = dyn_cast<Value>(ofr);
78-
IntegerAttr attr;
79-
if (matchPattern(value, m_Constant(&attr))) {
80-
rotationIndices.insert(attr.getInt());
88+
if (allConstant) {
89+
rotationIndices.insert(constantIndices.begin(), constantIndices.end());
8190
markVisited(rotationOp);
8291
return success();
8392
}

lib/Analysis/RotationAnalysis/RotationEvalVisitor.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "lib/Kernel/AbstractValue.h"
1313
#include "lib/Kernel/ArithmeticDag.h"
1414
#include "lib/Kernel/EvalVisitor.h"
15+
#include "lib/Utils/RotationUtils.h"
1516
#include "llvm/include/llvm/Support/ErrorHandling.h" // from @llvm-project
1617

1718
#define DEBUG_TYPE "rotation-analysis"
@@ -22,6 +23,7 @@ namespace heir {
2223
using kernel::ArithmeticDagNode;
2324
using kernel::DagType;
2425
using kernel::EvalResults;
26+
using kernel::LeftRotateBulkNode;
2527
using kernel::LeftRotateNode;
2628
using kernel::LiteralValue;
2729
using kernel::VariableNode;
@@ -38,15 +40,33 @@ EvalResults RotationEvalVisitor::operator()(
3840
auto evaluatedShift = this->process(node.shift)[0];
3941
int amount = std::get<int>(evaluatedShift.get());
4042

41-
// Normalize amount to be in [0, dim)
42-
amount = ((amount % dim) + dim) % dim;
43+
amount = normalizeRotation(amount, dim);
4344
evaluatedShifts.insert(amount);
4445

4546
// We don't need to rotate the values for rotation analysis. We just return
4647
// the operand as-is to keep the IR connected.
4748
return {operand};
4849
}
4950

51+
EvalResults RotationEvalVisitor::operator()(
52+
const LeftRotateBulkNode<LiteralValue>& node) {
53+
auto operand = this->process(node.operand)[0];
54+
auto shape = operand.getShape();
55+
assert(!shape.empty() && "rotate operand must be a tensor");
56+
auto dim = shape.back();
57+
58+
for (const auto& shiftNode : node.shifts) {
59+
auto evaluatedShift = this->process(shiftNode)[0];
60+
int amount = std::get<int>(evaluatedShift.get());
61+
amount = normalizeRotation(amount, dim);
62+
evaluatedShifts.insert(amount);
63+
}
64+
65+
// We don't need to rotate the values for rotation analysis. We just return
66+
// the operand as-is to keep the IR connected.
67+
return {operand};
68+
}
69+
5070
EvalResults RotationEvalVisitor::operator()(
5171
const VariableNode<LiteralValue>& node) {
5272
if (node.value.has_value()) {

lib/Analysis/RotationAnalysis/RotationEvalVisitor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class RotationEvalVisitor : public kernel::EvalVisitor {
2020
kernel::EvalResults operator()(
2121
const kernel::LeftRotateNode<kernel::LiteralValue>& node) override;
2222

23+
// Override the bulk rotation op to record all materialized rotation shifts.
24+
kernel::EvalResults operator()(
25+
const kernel::LeftRotateBulkNode<kernel::LiteralValue>& node) override;
26+
2327
// Override the variable node to allow uninitialized values to be populated
2428
// with anything.
2529
kernel::EvalResults operator()(

lib/Dialect/BGV/IR/BGVOps.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,15 @@ LogicalResult ModulusSwitchOp::verify() {
3636

3737
LogicalResult LevelReduceOp::verify() { return lwe::verifyLevelReduceOp(this); }
3838

39-
::mlir::OpFoldResult RotateColumnsOp::getRotationIndex() {
40-
if (getStaticShift()) return getStaticShiftAttr();
41-
return getDynamicShift();
39+
::llvm::SmallVector<::mlir::OpFoldResult>
40+
RotateColumnsOp::getRotationIndices() {
41+
if (getStaticShift()) return {getStaticShiftAttr()};
42+
return {getDynamicShift()};
4243
}
4344

44-
::mlir::OpFoldResult RotateRowsOp::getRotationIndex() {
45-
if (getStaticShift()) return getStaticShiftAttr();
46-
return getDynamicShift();
45+
::llvm::SmallVector<::mlir::OpFoldResult> RotateRowsOp::getRotationIndices() {
46+
if (getStaticShift()) return {getStaticShiftAttr()};
47+
return {getDynamicShift()};
4748
}
4849

4950
//===----------------------------------------------------------------------===//

lib/Dialect/CKKS/IR/CKKSOps.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ LogicalResult RescaleOp::verify() {
4747

4848
LogicalResult LevelReduceOp::verify() { return lwe::verifyLevelReduceOp(this); }
4949

50-
::mlir::OpFoldResult RotateOp::getRotationIndex() {
51-
if (getStaticShift()) return getStaticShiftAttr();
52-
return getDynamicShift();
50+
::llvm::SmallVector<::mlir::OpFoldResult> RotateOp::getRotationIndices() {
51+
if (getStaticShift()) return {getStaticShiftAttr()};
52+
return {getDynamicShift()};
5353
}
5454

5555
LogicalResult BootstrapOp::verify() {

lib/Dialect/HEIRInterfaces.td

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,13 +443,15 @@ def OperandAndResultAttrInterface : OpInterface<"OperandAndResultAttrInterface">
443443
def RotationOpInterface : OpInterface<"RotationOpInterface"> {
444444
let cppNamespace = "::mlir::heir";
445445
let description = [{
446-
An interface for rotation operations.
446+
An interface for operations that perform rotations, possibly as a subroutine
447+
of this op. This is used in the analysis step to determine what rotation
448+
keys need to be generated.
447449
}];
448450

449451
let methods = [
450452
InterfaceMethod<
451-
"Returns the rotation index as an OpFoldResult (Value or Attribute).",
452-
"::mlir::OpFoldResult", "getRotationIndex"
453+
"Returns the rotation indices as OpFoldResults (Values or Attributes).",
454+
"::llvm::SmallVector<::mlir::OpFoldResult>", "getRotationIndices"
453455
>,
454456

455457
InterfaceMethod<

lib/Dialect/Lattigo/IR/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ cc_library(
9090
":types_inc_gen",
9191
"@heir//lib/Dialect:HEIRInterfaces",
9292
"@heir//lib/Utils",
93+
"@heir//lib/Utils:RotationUtils",
9394
"@heir//lib/Utils/Tablegen:InPlaceOpInterface",
9495
"@llvm-project//mlir:IR",
9596
"@llvm-project//mlir:InferTypeOpInterface",

lib/Dialect/Lattigo/IR/LattigoCKKSOps.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,9 @@ def Lattigo_CKKSBootstrapOp : Lattigo_CKKSUnaryOp<"bootstrap"> {
341341
let results = (outs Lattigo_RLWECiphertext:$output);
342342
}
343343

344-
def Lattigo_CKKSLinearTransformOp : Lattigo_CKKSOp<"linear_transform"> {
344+
def Lattigo_CKKSLinearTransformOp : Lattigo_CKKSOp<"linear_transform", [
345+
DeclareOpInterfaceMethods<RotationOpInterface>
346+
]> {
345347
let summary = "Apply a linear transform on a lattigo CKKS ciphertext";
346348
let description = [{
347349
This operation applies a linear transform on a CKKS ciphertext using

0 commit comments

Comments
 (0)