https://github.com/xlauko updated https://github.com/llvm/llvm-project/pull/185276
>From b26d3e9212ec0e0b66b1adc6802e3612ccb5a21a Mon Sep 17 00:00:00 2001 From: xlauko <[email protected]> Date: Sun, 8 Mar 2026 13:40:09 +0100 Subject: [PATCH] [CIR] Add cir.min op and refactor cir.max lowering Add cir.min operation for integer minimum computation. Refactor cir.max lowering into a shared lowerMinMaxOp template reused by both ops. Includes lowering tests for signed, unsigned, and vector types, plus canonicalization tests. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 27 ++++++- .../Dialect/Transforms/CIRCanonicalize.cpp | 10 +-- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 25 ++++-- clang/test/CIR/Lowering/binop-int-vector.cir | 24 ++++++ clang/test/CIR/Lowering/binop-signed-int.cir | 2 + .../test/CIR/Lowering/binop-unsigned-int.cir | 3 + .../CIR/Transforms/max-min-idempotent.cir | 77 +++++++++++++++++++ 7 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 clang/test/CIR/Lowering/binop-int-vector.cir create mode 100644 clang/test/CIR/Transforms/max-min-idempotent.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 742f25461e60d..d9c4356ba95b9 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2400,13 +2400,38 @@ def CIR_MaxOp : CIR_BinaryOp<"max", CIR_AnyIntOrVecOfIntType, [ let summary = "Integer maximum"; let description = [{ The `cir.max` operation computes the maximum of two integer operands. - Both operands and the result must have the same integer type. + Both operands and the result must have the same integer type or vector of + integer type. Example: ```mlir %0 = cir.max %a, %b : !s32i %1 = cir.max %a, %b : !u32i + %2 = cir.max %a, %b : !cir.vector<4 x !s32i> + ``` + }]; +} + +//===----------------------------------------------------------------------===// +// MinOp +//===----------------------------------------------------------------------===// + +def CIR_MinOp : CIR_BinaryOp<"min", CIR_AnyIntOrVecOfIntType, [ + Commutative, Idempotent +]> { + let summary = "Integer minimum"; + let description = [{ + The `cir.min` operation computes the minimum of two integer operands. + Both operands and the result must have the same integer type or vector of + integer type. + + Example: + + ```mlir + %0 = cir.min %a, %b : !s32i + %1 = cir.min %a, %b : !u32i + %2 = cir.min %a, %b : !cir.vector<4 x !s32i> ``` }]; } diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp index 46a8c011b320b..c29e7585aa56d 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -71,11 +71,11 @@ void CIRCanonicalizePass::runOnOperation() { // Many operations are here to perform a manual `fold` in // applyOpPatternsGreedily. if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp, AddOp, - MulOp, AndOp, OrOp, XorOp, MaxOp, ComplexCreateOp, ComplexImagOp, - ComplexRealOp, VecCmpOp, VecCreateOp, VecExtractOp, VecShuffleOp, - VecShuffleDynamicOp, VecTernaryOp, BitClrsbOp, BitClzOp, BitCtzOp, - BitFfsOp, BitParityOp, BitPopcountOp, BitReverseOp, ByteSwapOp, - RotateOp, ConstantOp>(op)) + MulOp, AndOp, OrOp, XorOp, MaxOp, MinOp, ComplexCreateOp, + ComplexImagOp, ComplexRealOp, VecCmpOp, VecCreateOp, VecExtractOp, + VecShuffleOp, VecShuffleDynamicOp, VecTernaryOp, BitClrsbOp, + BitClzOp, BitCtzOp, BitFfsOp, BitParityOp, BitPopcountOp, + BitReverseOp, ByteSwapOp, RotateOp, ConstantOp>(op)) ops.push_back(op); }); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 9c68248d5dede..8f80b1ee5116e 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2998,18 +2998,33 @@ mlir::LogicalResult CIRToLLVMXorOpLowering::matchAndRewrite( return mlir::success(); } -mlir::LogicalResult CIRToLLVMMaxOpLowering::matchAndRewrite( - cir::MaxOp op, OpAdaptor adaptor, - mlir::ConversionPatternRewriter &rewriter) const { +template <typename CIROp, typename UIntOp, typename SIntOp> +static mlir::LogicalResult +lowerMinMaxOp(CIROp op, typename CIROp::Adaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) { const mlir::Value lhs = adaptor.getLhs(); const mlir::Value rhs = adaptor.getRhs(); if (isIntTypeUnsigned(elementTypeIfVector(op.getRhs().getType()))) - rewriter.replaceOpWithNewOp<mlir::LLVM::UMaxOp>(op, lhs, rhs); + rewriter.replaceOpWithNewOp<UIntOp>(op, lhs, rhs); else - rewriter.replaceOpWithNewOp<mlir::LLVM::SMaxOp>(op, lhs, rhs); + rewriter.replaceOpWithNewOp<SIntOp>(op, lhs, rhs); return mlir::success(); } +mlir::LogicalResult CIRToLLVMMaxOpLowering::matchAndRewrite( + cir::MaxOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return lowerMinMaxOp<cir::MaxOp, mlir::LLVM::UMaxOp, mlir::LLVM::SMaxOp>( + op, adaptor, rewriter); +} + +mlir::LogicalResult CIRToLLVMMinOpLowering::matchAndRewrite( + cir::MinOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return lowerMinMaxOp<cir::MinOp, mlir::LLVM::UMinOp, mlir::LLVM::SMinOp>( + op, adaptor, rewriter); +} + /// Convert from a CIR comparison kind to an LLVM IR integral comparison kind. static mlir::LLVM::ICmpPredicate convertCmpKindToICmpPredicate(cir::CmpOpKind kind, bool isSigned) { diff --git a/clang/test/CIR/Lowering/binop-int-vector.cir b/clang/test/CIR/Lowering/binop-int-vector.cir new file mode 100644 index 0000000000000..d8814669b1fb2 --- /dev/null +++ b/clang/test/CIR/Lowering/binop-int-vector.cir @@ -0,0 +1,24 @@ +// RUN: cir-opt %s -cir-to-llvm -o - | FileCheck %s + +!s32i = !cir.int<s, 32> +!u32i = !cir.int<u, 32> + +module { + cir.func @signed_vec(%arg0 : !cir.vector<4 x !s32i>, + %arg1 : !cir.vector<4 x !s32i>) { + %0 = cir.max %arg0, %arg1 : !cir.vector<4 x !s32i> + // CHECK: = llvm.intr.smax + %1 = cir.min %arg0, %arg1 : !cir.vector<4 x !s32i> + // CHECK: = llvm.intr.smin + cir.return + } + + cir.func @unsigned_vec(%arg0 : !cir.vector<4 x !u32i>, + %arg1 : !cir.vector<4 x !u32i>) { + %0 = cir.max %arg0, %arg1 : !cir.vector<4 x !u32i> + // CHECK: = llvm.intr.umax + %1 = cir.min %arg0, %arg1 : !cir.vector<4 x !u32i> + // CHECK: = llvm.intr.umin + cir.return + } +} diff --git a/clang/test/CIR/Lowering/binop-signed-int.cir b/clang/test/CIR/Lowering/binop-signed-int.cir index 091becbd78c2c..15bee750e2d7c 100644 --- a/clang/test/CIR/Lowering/binop-signed-int.cir +++ b/clang/test/CIR/Lowering/binop-signed-int.cir @@ -55,6 +55,8 @@ module { cir.store %34, %2 : !s32i, !cir.ptr<!s32i> %37 = cir.max %32, %33 : !s32i // CHECK: = llvm.intr.smax + %38 = cir.min %32, %33 : !s32i + // CHECK: = llvm.intr.smin cir.return } } diff --git a/clang/test/CIR/Lowering/binop-unsigned-int.cir b/clang/test/CIR/Lowering/binop-unsigned-int.cir index 79d8f6bdcecd3..d5545cb7769d4 100644 --- a/clang/test/CIR/Lowering/binop-unsigned-int.cir +++ b/clang/test/CIR/Lowering/binop-unsigned-int.cir @@ -44,6 +44,7 @@ module { %35 = cir.add sat %32, %33: !u32i %36 = cir.sub sat %32, %33: !u32i %37 = cir.max %32, %33 : !u32i + %38 = cir.min %32, %33 : !u32i cir.return } } @@ -59,6 +60,7 @@ module { // MLIR: = llvm.intr.uadd.sat{{.*}}(i32, i32) -> i32 // MLIR: = llvm.intr.usub.sat{{.*}}(i32, i32) -> i32 // MLIR: = llvm.intr.umax +// MLIR: = llvm.intr.umin // LLVM: = mul i32 // LLVM: = udiv i32 @@ -71,3 +73,4 @@ module { // LLVM: = call i32 @llvm.uadd.sat.i32 // LLVM: = call i32 @llvm.usub.sat.i32 // LLVM: = call i32 @llvm.umax.i32 +// LLVM: = call i32 @llvm.umin.i32 diff --git a/clang/test/CIR/Transforms/max-min-idempotent.cir b/clang/test/CIR/Transforms/max-min-idempotent.cir new file mode 100644 index 0000000000000..1ce4ced3f745d --- /dev/null +++ b/clang/test/CIR/Transforms/max-min-idempotent.cir @@ -0,0 +1,77 @@ +// RUN: cir-opt %s -cir-canonicalize -o - | FileCheck %s + +!s32i = !cir.int<s, 32> +!u32i = !cir.int<u, 32> + +// Idempotent: max(x, x) -> x +// CHECK-LABEL: cir.func @max_idempotent +// CHECK-NEXT: cir.return %arg0 +cir.func @max_idempotent(%arg0 : !s32i) -> !s32i { + %0 = cir.max %arg0, %arg0 : !s32i + cir.return %0 : !s32i +} + +// Idempotent: min(x, x) -> x +// CHECK-LABEL: cir.func @min_idempotent +// CHECK-NEXT: cir.return %arg0 +cir.func @min_idempotent(%arg0 : !s32i) -> !s32i { + %0 = cir.min %arg0, %arg0 : !s32i + cir.return %0 : !s32i +} + +// Idempotent: max(x, x) -> x (unsigned) +// CHECK-LABEL: cir.func @max_idempotent_unsigned +// CHECK-NEXT: cir.return %arg0 +cir.func @max_idempotent_unsigned(%arg0 : !u32i) -> !u32i { + %0 = cir.max %arg0, %arg0 : !u32i + cir.return %0 : !u32i +} + +// Idempotent: min(x, x) -> x (unsigned) +// CHECK-LABEL: cir.func @min_idempotent_unsigned +// CHECK-NEXT: cir.return %arg0 +cir.func @min_idempotent_unsigned(%arg0 : !u32i) -> !u32i { + %0 = cir.min %arg0, %arg0 : !u32i + cir.return %0 : !u32i +} + +// Commutative: max(const, x) -> max(x, const) +// CHECK-LABEL: cir.func @max_commutative +// CHECK: %[[C:.*]] = cir.const #cir.int<42> : !s32i +// CHECK-NEXT: %[[R:.*]] = cir.max %arg0, %[[C]] : !s32i +// CHECK-NEXT: cir.return %[[R]] +cir.func @max_commutative(%arg0 : !s32i) -> !s32i { + %0 = cir.const #cir.int<42> : !s32i + %1 = cir.max %0, %arg0 : !s32i + cir.return %1 : !s32i +} + +// Commutative: min(const, x) -> min(x, const) +// CHECK-LABEL: cir.func @min_commutative +// CHECK: %[[C:.*]] = cir.const #cir.int<42> : !s32i +// CHECK-NEXT: %[[R:.*]] = cir.min %arg0, %[[C]] : !s32i +// CHECK-NEXT: cir.return %[[R]] +cir.func @min_commutative(%arg0 : !s32i) -> !s32i { + %0 = cir.const #cir.int<42> : !s32i + %1 = cir.min %0, %arg0 : !s32i + cir.return %1 : !s32i +} + +// Idempotent chained: max(max(x, y), max(x, y)) -> max(x, y) +// CHECK-LABEL: cir.func @max_idempotent_chained +// CHECK-NEXT: %[[M:.*]] = cir.max %arg0, %arg1 : !s32i +// CHECK-NEXT: cir.return %[[M]] +cir.func @max_idempotent_chained(%arg0 : !s32i, %arg1 : !s32i) -> !s32i { + %0 = cir.max %arg0, %arg1 : !s32i + %1 = cir.max %0, %0 : !s32i + cir.return %1 : !s32i +} + +// No fold: distinct operands should remain unchanged +// CHECK-LABEL: cir.func @max_no_fold +// CHECK-NEXT: %[[R:.*]] = cir.max %arg0, %arg1 : !s32i +// CHECK-NEXT: cir.return %[[R]] +cir.func @max_no_fold(%arg0 : !s32i, %arg1 : !s32i) -> !s32i { + %0 = cir.max %arg0, %arg1 : !s32i + cir.return %0 : !s32i +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
