https://github.com/prometheusfma-llvm created https://github.com/llvm/llvm-project/pull/218398
Fold scalar integer operands while honoring overflow flags, saturated arithmetic, and poison propagation. Add canonicalization coverage for the new folds. >From aeaad545e849f029b8d9c138f0c45524dae29a2e Mon Sep 17 00:00:00 2001 From: Prometheus <[email protected]> Date: Mon, 24 Aug 2026 05:56:35 -0700 Subject: [PATCH] [CIR] Fold constant add and sub operations Fold scalar integer operands while honoring overflow flags, saturated arithmetic, and poison propagation. Add canonicalization coverage for the new folds. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 4 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 65 +++++++++++++++ .../Dialect/Transforms/CIRCanonicalize.cpp | 4 +- clang/test/CIR/Transforms/canonicalize.cir | 82 +++++++++++++++++++ 4 files changed, 153 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index a52c37e4860ce..2065042d92326 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2764,6 +2764,8 @@ def CIR_AddOp %4 = cir.add %va, %vb : !cir.vector<4 x !s32i> ``` }]; + + let hasFolder = 1; } //===----------------------------------------------------------------------===// @@ -2793,6 +2795,8 @@ def CIR_SubOp %3 = cir.sub %va, %vb : !cir.vector<4 x !s32i> ``` }]; + + let hasFolder = 1; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 66d7feebc6372..a67c6fa717d43 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -3185,6 +3185,71 @@ OpFoldResult cir::NotOp::fold(FoldAdaptor adaptor) { return {}; } +//===----------------------------------------------------------------------===// +// AddOp & SubOp +//===----------------------------------------------------------------------===// + +// Constant-fold integer add/sub. Honors nsw/nuw by folding to poison on the +// corresponding overflow, and folds saturated arithmetic by clamping. +static OpFoldResult foldAddSubConst(mlir::Type ty, const APInt &lhs, + const APInt &rhs, bool isSub, bool nsw, + bool nuw, bool sat) { + bool isSigned = mlir::cast<cir::IntType>(ty).isSigned(); + if (sat) { + APInt res = isSub ? (isSigned ? lhs.ssub_sat(rhs) : lhs.usub_sat(rhs)) + : (isSigned ? lhs.sadd_sat(rhs) : lhs.uadd_sat(rhs)); + return cir::IntAttr::get(ty, res); + } + + bool poison = false; + if (nsw) { + bool overflow = false; + (void)(isSub ? lhs.ssub_ov(rhs, overflow) : lhs.sadd_ov(rhs, overflow)); + poison |= overflow; + } + if (nuw) { + bool overflow = false; + (void)(isSub ? lhs.usub_ov(rhs, overflow) : lhs.uadd_ov(rhs, overflow)); + poison |= overflow; + } + if (poison) + return cir::PoisonAttr::get(ty); + + return cir::IntAttr::get(ty, isSub ? (lhs - rhs) : (lhs + rhs)); +} + +OpFoldResult cir::AddOp::fold(FoldAdaptor adaptor) { + if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getLhs())) + return adaptor.getLhs(); + if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getRhs())) + return adaptor.getRhs(); + + auto lhs = mlir::dyn_cast_if_present<cir::IntAttr>(adaptor.getLhs()); + auto rhs = mlir::dyn_cast_if_present<cir::IntAttr>(adaptor.getRhs()); + if (!lhs || !rhs) + return {}; + + return foldAddSubConst(getType(), lhs.getValue(), rhs.getValue(), + /*isSub=*/false, getNoSignedWrap(), + getNoUnsignedWrap(), getSaturated()); +} + +OpFoldResult cir::SubOp::fold(FoldAdaptor adaptor) { + if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getLhs())) + return adaptor.getLhs(); + if (mlir::isa_and_present<cir::PoisonAttr>(adaptor.getRhs())) + return adaptor.getRhs(); + + auto lhs = mlir::dyn_cast_if_present<cir::IntAttr>(adaptor.getLhs()); + auto rhs = mlir::dyn_cast_if_present<cir::IntAttr>(adaptor.getRhs()); + if (!lhs || !rhs) + return {}; + + return foldAddSubConst(getType(), lhs.getValue(), rhs.getValue(), + /*isSub=*/true, getNoSignedWrap(), getNoUnsignedWrap(), + getSaturated()); +} + //===----------------------------------------------------------------------===// // BaseDataMemberOp & DerivedDataMemberOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp index b4890a1b5bf37..ed84ac1458314 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -71,8 +71,8 @@ void CIRCanonicalizePass::runOnOperation() { // Many operations are here to perform a manual `fold` in // applyOpPatternsGreedily. if (isa<BrOp, BrCondOp, BuiltinIntCastOp, CastOp, ScopeOp, SwitchOp, - SelectOp, IncOp, DecOp, MinusOp, FNegOp, NotOp, AddOp, MulOp, AndOp, - OrOp, XorOp, MaxOp, MinOp, ComplexCreateOp, ComplexImagOp, + SelectOp, IncOp, DecOp, MinusOp, FNegOp, NotOp, AddOp, SubOp, MulOp, + AndOp, OrOp, XorOp, MaxOp, MinOp, ComplexCreateOp, ComplexImagOp, ComplexRealOp, VecCmpOp, VecCreateOp, VecExtractOp, VecShuffleOp, VecShuffleDynamicOp, VecTernaryOp, BitClrsbOp, BitClzOp, BitCtzOp, BitFfsOp, BitParityOp, BitPopcountOp, BitReverseOp, ByteSwapOp, diff --git a/clang/test/CIR/Transforms/canonicalize.cir b/clang/test/CIR/Transforms/canonicalize.cir index 29b1d880a3748..6ff0da22ccee7 100644 --- a/clang/test/CIR/Transforms/canonicalize.cir +++ b/clang/test/CIR/Transforms/canonicalize.cir @@ -323,4 +323,86 @@ module { // CHECK-NEXT: cir.return %[[P]] : !s32i // CHECK-NEXT: } + cir.func @add_int() -> !s32i { + %0 = cir.const #cir.int<3> : !s32i + %1 = cir.const #cir.int<4> : !s32i + %2 = cir.add %0, %1 : !s32i + cir.return %2 : !s32i + } + // CHECK: @add_int() + // CHECK-NEXT: %[[C:.+]] = cir.const #cir.int<7> : !s32i + // CHECK-NEXT: cir.return %[[C]] : !s32i + // CHECK-NEXT: } + + cir.func @sub_int() -> !s32i { + %0 = cir.const #cir.int<10> : !s32i + %1 = cir.const #cir.int<4> : !s32i + %2 = cir.sub %0, %1 : !s32i + cir.return %2 : !s32i + } + // CHECK: @sub_int() + // CHECK-NEXT: %[[C:.+]] = cir.const #cir.int<6> : !s32i + // CHECK-NEXT: cir.return %[[C]] : !s32i + // CHECK-NEXT: } + + // Without wrap flags the result wraps in two's complement. + cir.func @add_wrap() -> !s8i { + %0 = cir.const #cir.int<127> : !s8i + %1 = cir.const #cir.int<1> : !s8i + %2 = cir.add %0, %1 : !s8i + cir.return %2 : !s8i + } + // CHECK: @add_wrap() + // CHECK-NEXT: %[[C:.+]] = cir.const #cir.int<-128> : !s8i + // CHECK-NEXT: cir.return %[[C]] : !s8i + // CHECK-NEXT: } + + // nsw overflow folds to poison. + cir.func @add_nsw_overflow() -> !s8i { + %0 = cir.const #cir.int<127> : !s8i + %1 = cir.const #cir.int<1> : !s8i + %2 = cir.add nsw %0, %1 : !s8i + cir.return %2 : !s8i + } + // CHECK: @add_nsw_overflow() + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !s8i + // CHECK-NEXT: cir.return %[[P]] : !s8i + // CHECK-NEXT: } + + // Saturated add clamps to the signed maximum. + cir.func @add_sat_signed() -> !s8i { + %0 = cir.const #cir.int<127> : !s8i + %1 = cir.const #cir.int<10> : !s8i + %2 = cir.add sat %0, %1 : !s8i + cir.return %2 : !s8i + } + // CHECK: @add_sat_signed() + // CHECK-NEXT: %[[C:.+]] = cir.const #cir.int<127> : !s8i + // CHECK-NEXT: cir.return %[[C]] : !s8i + // CHECK-NEXT: } + + // A poison operand propagates to the result. + cir.func @add_poison() -> !s32i { + %0 = cir.const #cir.poison : !s32i + %1 = cir.const #cir.int<1> : !s32i + %2 = cir.add %0, %1 : !s32i + cir.return %2 : !s32i + } + // CHECK: @add_poison() + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !s32i + // CHECK-NEXT: cir.return %[[P]] : !s32i + // CHECK-NEXT: } + + // nuw overflow on subtraction folds to poison. + cir.func @sub_nuw_overflow() -> !u8i { + %0 = cir.const #cir.int<0> : !u8i + %1 = cir.const #cir.int<1> : !u8i + %2 = cir.sub nuw %0, %1 : !u8i + cir.return %2 : !u8i + } + // CHECK: @sub_nuw_overflow() + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !u8i + // CHECK-NEXT: cir.return %[[P]] : !u8i + // CHECK-NEXT: } + } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
