Skip to content

Commit cbf0016

Browse files
Merge pull request #3316 from google:mdgrs/compositeRelu
PiperOrigin-RevId: 960967432
2 parents 89905fc + 447b3f7 commit cbf0016

17 files changed

Lines changed: 566 additions & 28 deletions

File tree

lib/Pipelines/ArithmeticPipelineRegistration.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ void mlirToSecretArithmeticPipelineBuilder(
201201
// Vectorize and optimize rotations
202202
// TODO(#2320): figure out where this fits in the new pipeline
203203
hecoSIMDVectorizerPipelineBuilder(pm, options.experimentalDisableLoopUnroll);
204-
mathToPolynomialApproximationBuilder(pm);
204+
mathToPolynomialApproximationBuilder(pm, options.useCompositeRelu);
205205

206206
// Layout assignment and optimization
207207
LayoutPropagationOptions layoutPropagationOptions;
@@ -715,6 +715,7 @@ void torchLinalgToCkksBuilder(OpPassManager& manager,
715715
suboptions.enableArithmetization = true;
716716
suboptions.minSlotCount = options.minSlotCount;
717717
suboptions.greedyBootstrapWaterline = options.greedyBootstrapWaterline;
718+
suboptions.useCompositeRelu = options.useCompositeRelu;
718719
suboptions.scalingModBits = options.scalingModBits;
719720
suboptions.firstModBits = options.firstModBits;
720721
suboptions.enableSplitPreprocessing = options.enableSplitPreprocessing;

lib/Pipelines/ArithmeticPipelineRegistration.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ struct MlirToRLWEPipelineOptions : public LoopOptions {
7777
*this, "bfv-mod-bits",
7878
llvm::cl::desc("The number of bits for all moduli for B/FV"),
7979
llvm::cl::init(60)};
80+
PassOptions::Option<bool> useCompositeRelu{
81+
*this, "use-composite-relu",
82+
llvm::cl::desc("Approximate ReLU with the composite-sign method "
83+
"(x*step(x/B), 3 chained minimax Chebyshev polys) "
84+
"instead of a single-polynomial max(x,0) fit. More "
85+
"accurate for deep nets; needs more depth/bootstrapping."),
86+
llvm::cl::init(false)};
8087
PassOptions::Option<bool> debug{
8188
*this, "debug",
8289
llvm::cl::desc("Insert debug ports after every secret operation."),

lib/Pipelines/PipelineRegistration.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@ void oneShotBufferize(OpPassManager& manager, bool includeDeallocation) {
6666
manager.addPass(createCanonicalizerPass());
6767
}
6868

69-
void mathToPolynomialApproximationBuilder(OpPassManager& pm) {
70-
pm.addPass(createPolynomialApproximation());
69+
void mathToPolynomialApproximationBuilder(OpPassManager& pm,
70+
bool useCompositeRelu) {
71+
PolynomialApproximationOptions polyApproxOptions;
72+
polyApproxOptions.useCompositeRelu = useCompositeRelu;
73+
pm.addPass(createPolynomialApproximation(polyApproxOptions));
7174
pm.addPass(createLowerPolynomialEval());
7275
pm.addPass(createCanonicalizerPass());
7376
pm.addPass(createCSEPass());

lib/Pipelines/PipelineRegistration.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ void prepareForBufferize(OpPassManager& manager);
1414

1515
void oneShotBufferize(OpPassManager& manager, bool includeDeallocation = true);
1616

17-
void mathToPolynomialApproximationBuilder(OpPassManager& pm);
17+
void mathToPolynomialApproximationBuilder(OpPassManager& pm,
18+
bool useCompositeRelu = false);
1819

1920
void polynomialToLLVMPipelineBuilder(OpPassManager& manager);
2021

lib/Transforms/ActivationCanonicalizations/ActivationCanonicalizations.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "lib/Dialect/MathExt/IR/MathExtOps.h"
66
#include "llvm/include/llvm/ADT/APFloat.h" // from @llvm-project
7+
#include "mlir/include/mlir/Dialect/Arith/IR/Arith.h" // from @llvm-project
8+
#include "mlir/include/mlir/Dialect/Linalg/IR/Linalg.h" // from @llvm-project
79
#include "mlir/include/mlir/IR/Attributes.h" // from @llvm-project
810
#include "mlir/include/mlir/IR/BuiltinAttributes.h" // from @llvm-project
911
#include "mlir/include/mlir/IR/BuiltinTypeInterfaces.h" // from @llvm-project
@@ -40,6 +42,93 @@ static bool IsOne(mlir::Attribute attr) {
4042
// populateWithGenerated, which can conflict with other generated patterns.
4143
#include "lib/Transforms/ActivationCanonicalizations/Rewrites.cpp.inc"
4244

45+
// select(a > c, a, c) = max(a, c) for floats. This replaces the DRR
46+
// `SelectGreaterThanEqualFloat` pattern and folds in
47+
// two attr-forwarding behaviors so the polynomial-approximation domain survives
48+
// regardless of where torch-mlir attached it:
49+
// (a) copy discardable attrs off the select itself (the old DRR behavior),
50+
// (b) if the domain is still missing, copy `domain_lower`/`domain_upper` from
51+
// an enclosing `linalg.generic` (a torch ReLU imports as a generic
52+
// carrying those attrs, with `cmpf+select` in its body) The
53+
// generic's copy is dropped afterwards by stripForwardedDomains().
54+
// Either way the domain lands on the `arith.maximumf` that
55+
// PolynomialApproximation / ReluViaCompositeSign read; without it they fall
56+
// back to [-1, 1].
57+
struct SelectGreaterThanEqualFloatPattern
58+
: public OpRewritePattern<arith::SelectOp> {
59+
using OpRewritePattern<arith::SelectOp>::OpRewritePattern;
60+
61+
LogicalResult matchAndRewrite(arith::SelectOp op,
62+
PatternRewriter& rewriter) const override {
63+
auto cmpOp = op.getCondition().getDefiningOp<arith::CmpFOp>();
64+
if (!cmpOp)
65+
return rewriter.notifyMatchFailure(op, "condition is not arith.cmpf");
66+
67+
auto pred = cmpOp.getPredicate();
68+
if (pred != arith::CmpFPredicate::UGT && pred != arith::CmpFPredicate::UGE)
69+
return rewriter.notifyMatchFailure(op, "predicate is not ugt/uge");
70+
71+
// Must be the ReLU/max shape: select(a >? c, a, c).
72+
if (cmpOp.getLhs() != op.getTrueValue() ||
73+
cmpOp.getRhs() != op.getFalseValue())
74+
return rewriter.notifyMatchFailure(op,
75+
"operands are not select(a>c,a,c)");
76+
77+
auto maxOp =
78+
arith::MaximumFOp::create(rewriter, op.getLoc(), op.getTrueValue(),
79+
op.getFalseValue(), cmpOp.getFastmathAttr());
80+
81+
// (a) Forward any discardable attrs annotated on the select op itself onto
82+
// the maximumf (the old DRR `SelectGreaterThanEqualFloat` behavior). Covers
83+
// IR where the domain is attached directly to the select.
84+
for (auto attr : op->getDiscardableAttrs())
85+
maxOp->setAttr(attr.getName(), attr.getValue());
86+
87+
// (b) If the domain still isn't on the maximumf, it lives on an enclosing
88+
// linalg.generic instead (where torch-mlir's importer attaches the ReLU
89+
// domain). Copy it down onto the maximumf — the op PolynomialApproximation
90+
// actually reads. We only COPY here; the generic's own bounds are dropped
91+
// afterwards by stripForwardedDomains(). A single generic can hold several
92+
// ReLUs, so stripping as soon as the first one is rewritten would starve
93+
// the rest and silently leave them on the default [-1, 1] domain.
94+
if (auto generic = dyn_cast<linalg::GenericOp>(op->getParentOp())) {
95+
Attribute lo = generic->getAttr("domain_lower");
96+
Attribute hi = generic->getAttr("domain_upper");
97+
if (lo && !maxOp->hasAttr("domain_lower"))
98+
maxOp->setAttr("domain_lower", lo);
99+
if (hi && !maxOp->hasAttr("domain_upper"))
100+
maxOp->setAttr("domain_upper", hi);
101+
}
102+
103+
rewriter.replaceOp(op, maxOp.getResult());
104+
return success();
105+
}
106+
};
107+
108+
// The domain bounds must end up on exactly one op. Once the patterns above have
109+
// copied an enclosing generic's bounds down onto every ReLU in its body, the
110+
// generic's own copy is redundant, so drop it: leaving the bounds on BOTH makes
111+
// a later activation-lifting pass merge two `domain_lower` entries into one
112+
// dictionary, tripping DictionaryAttr's uniqueness assertion.
113+
static void stripForwardedDomains(Operation* root) {
114+
root->walk([](linalg::GenericOp generic) {
115+
if (!generic->hasAttr("domain_lower") && !generic->hasAttr("domain_upper"))
116+
return;
117+
// Only strip if the bounds actually made it onto an op inside the body;
118+
// otherwise there was no ReLU to forward them to and they are still the
119+
// only record of the domain.
120+
bool forwarded = false;
121+
generic->getRegion(0).walk([&](Operation* inner) {
122+
if (inner->hasAttr("domain_lower") || inner->hasAttr("domain_upper"))
123+
forwarded = true;
124+
});
125+
if (forwarded) {
126+
generic->removeAttr("domain_lower");
127+
generic->removeAttr("domain_upper");
128+
}
129+
});
130+
}
131+
43132
struct ActivationCanonicalizations
44133
: impl::ActivationCanonicalizationsBase<ActivationCanonicalizations> {
45134
using ActivationCanonicalizationsBase::ActivationCanonicalizationsBase;
@@ -48,8 +137,11 @@ struct ActivationCanonicalizations
48137
MLIRContext* context = &getContext();
49138
RewritePatternSet patterns(context);
50139
populateWithGenerated(patterns);
140+
patterns.add<SelectGreaterThanEqualFloatPattern>(context);
51141

52142
(void)walkAndApplyPatterns(getOperation(), std::move(patterns));
143+
144+
stripForwardedDomains(getOperation());
53145
}
54146
};
55147

lib/Transforms/ActivationCanonicalizations/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ cc_library(
3131
"@llvm-project//llvm:Support",
3232
"@llvm-project//mlir:ArithDialect",
3333
"@llvm-project//mlir:IR",
34+
"@llvm-project//mlir:LinalgDialect",
3435
"@llvm-project//mlir:MathDialect",
3536
"@llvm-project//mlir:Pass",
3637
"@llvm-project//mlir:Support",

lib/Transforms/ActivationCanonicalizations/Rewrites.td

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,8 @@ include "lib/Dialect/MathExt/IR/MathExtOps.td"
99

1010
def IsOne : Constraint<CPred<"IsOne($0)">>;
1111

12-
def GetDefiningOp : NativeCodeCall<"$0.getDefiningOp()">;
13-
14-
// These are attributes that are not inherent to the definition of the op, e.g.
15-
// domain_upper and domain_lower for activation functions.
16-
def CopyDiscardableAttrs : NativeCodeCallVoid<
17-
"for (auto attr : $0->getDiscardableAttrs()) $1->setAttr(attr.getName(), attr.getValue())">;
18-
19-
// select(a >= c, a, c) = max(a, c)
20-
def SelectGreaterThanEqualFloat : Pat<
21-
(SelectOp:$src
22-
(Arith_CmpFOp $pred, $a, $c, $flags), $a, $c),
23-
(Arith_MaximumFOp:$dest $a, $c, $flags),
24-
[(Constraint<
25-
CPred<"$0.getValue() == arith::CmpFPredicate::UGT || "
26-
"$0.getValue() == arith::CmpFPredicate::UGE">> $pred)],
27-
[(CopyDiscardableAttrs (GetDefiningOp $src), (GetDefiningOp $dest))]>;
12+
// select(a >= c, a, c) = max(a, c) for floats is handled by the C++
13+
// `SelectGreaterThanEqualFloatPattern` in ActivationCanonicalizations.cpp.
2814

2915
// select(a >= c, a, c) = max(a, c)
3016
def SelectGreaterThanEqualUnsigned : Pat<

lib/Transforms/PolynomialApproximation/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ cc_library(
1515
"@heir//lib/Analysis/SecretnessAnalysis",
1616
"@heir//lib/Dialect/MathExt/IR:Dialect",
1717
"@heir//lib/Dialect/Polynomial/IR:Dialect",
18+
"@heir//lib/Utils",
1819
"@heir//lib/Utils/Approximation:CaratheodoryFejer",
1920
"@heir//lib/Utils/Polynomial",
2021
"@llvm-project//llvm:Support",

0 commit comments

Comments
 (0)