From 243caebf042df9aa1d8d745d787a11b19a6bb809 Mon Sep 17 00:00:00 2001 From: Sirui Mu Date: Sat, 20 Apr 2024 23:50:59 +0800 Subject: [PATCH] [CIR] Lower certain cir.cmp3way operations to corresponding LLVM intrinsics LLVM recently added two families of intrinsics named `llvm.scmp.*` and `llvm.ucmp.*` that generate potentially better code for three-way comparison operations. This patch lowers certain `cir.cmp3way` operations to these intrinsics. Not all `cir.cmp3way` operations can be lowered to these intrinsics. The qualifying conditions are: 1) the comparison is between two integers, and 2) the comparison produces a strong order. `cir.cmp3way` operations that are not qualified are not affected by this patch. Qualifying `cir.cmp3way` operations may still need some canonicalization work before lowering. The "canonicalized" form of a qualifying three-way comparison operation yields -1 for lt, 0 for eq, and 1 for gt. This patch converts those non-canonicalized but qualifying `cir.cmp3way` operations to their canonical forms in the LLVM lowering prepare pass. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 5 + clang/include/clang/CIR/Dialect/IR/CIROps.td | 12 ++ clang/lib/CIR/CodeGen/CIRGenBuilder.h | 5 - .../Dialect/Transforms/LoweringPrepare.cpp | 72 ++++++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 86 ++++++++++++-- .../CodeGen/Inputs/std-compare-noncanonical.h | 110 ++++++++++++++++++ .../three-way-comparison-noncanonical.cpp | 42 +++++++ .../test/CIR/CodeGen/three-way-comparison.cpp | 22 +--- clang/test/CIR/Lowering/cmp3way.cir | 40 +++++++ 9 files changed, 361 insertions(+), 33 deletions(-) create mode 100644 clang/test/CIR/CodeGen/Inputs/std-compare-noncanonical.h create mode 100644 clang/test/CIR/CodeGen/three-way-comparison-noncanonical.cpp create mode 100644 clang/test/CIR/Lowering/cmp3way.cir diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 659819b17ab2..6312ee3ffd0e 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -78,6 +78,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { mlir::cir::UnaryOpKind::Not, value); } + mlir::cir::CmpOp createCompare(mlir::Location loc, mlir::cir::CmpOpKind kind, + mlir::Value lhs, mlir::Value rhs) { + return create(loc, getBoolTy(), kind, lhs, rhs); + } + mlir::Value createBinop(mlir::Value lhs, mlir::cir::BinOpKind kind, const llvm::APInt &rhs) { return create( diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index b89ba14f1451..301af02d62c3 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1328,6 +1328,18 @@ def CmpThreeWayOp : CIR_Op<"cmp3way", [Pure, SameTypeOperands]> { }]; let hasVerifier = 0; + + let extraClassDeclaration = [{ + /// Determine whether this three-way comparison produces a strong ordering. + bool isStrongOrdering() { + return getInfo().getOrdering() == mlir::cir::CmpOrdering::Strong; + } + + /// Determine whether this three-way comparison compares integral operands. + bool isIntegralComparison() { + return getLhs().getType().isa(); + } + }]; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 200d8d16b918..e5a7faab25a5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -594,11 +594,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { return create(loc); } - mlir::cir::CmpOp createCompare(mlir::Location loc, mlir::cir::CmpOpKind kind, - mlir::Value lhs, mlir::Value rhs) { - return create(loc, getBoolTy(), kind, lhs, rhs); - } - mlir::cir::MemCpyOp createMemCpy(mlir::Location loc, mlir::Value dst, mlir::Value src, mlir::Value len) { return create(loc, dst, src, len); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 00ec54867c9f..059a5b82167f 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -265,10 +265,82 @@ FuncOp LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(GlobalOp op) { return f; } +static void canonicalizeIntrinsicThreeWayCmp(CIRBaseBuilderTy &builder, + CmpThreeWayOp op) { + auto loc = op->getLoc(); + auto cmpInfo = op.getInfo(); + + if (cmpInfo.getLt() == -1 && cmpInfo.getEq() == 0 && cmpInfo.getGt() == 1) { + // The comparison is already in canonicalized form. + return; + } + + auto canonicalizedCmpInfo = + mlir::cir::CmpThreeWayInfoAttr::get(builder.getContext(), -1, 0, 1); + mlir::Value result = + builder + .create(loc, op.getType(), op.getLhs(), + op.getRhs(), canonicalizedCmpInfo) + .getResult(); + + auto compareAndYield = [&](mlir::Value input, int64_t test, + int64_t yield) -> mlir::Value { + // Create a conditional branch that tests whether `input` is equal to + // `test`. If `input` is equal to `test`, yield `yield`. Otherwise, yield + // `input` as is. + auto testValue = builder.getConstant( + loc, mlir::cir::IntAttr::get(input.getType(), test)); + auto yieldValue = builder.getConstant( + loc, mlir::cir::IntAttr::get(input.getType(), yield)); + auto eqToTest = + builder.createCompare(loc, mlir::cir::CmpOpKind::eq, input, testValue); + return builder + .create( + loc, eqToTest, + [&](OpBuilder &, Location) { + builder.create(loc, + mlir::ValueRange{yieldValue}); + }, + [&](OpBuilder &, Location) { + builder.create(loc, mlir::ValueRange{input}); + }) + ->getResult(0); + }; + + if (cmpInfo.getLt() != -1) + result = compareAndYield(result, -1, cmpInfo.getLt()); + + if (cmpInfo.getEq() != 0) + result = compareAndYield(result, 0, cmpInfo.getEq()); + + if (cmpInfo.getGt() != 1) + result = compareAndYield(result, 1, cmpInfo.getGt()); + + op.replaceAllUsesWith(result); + op.erase(); +} + void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) { CIRBaseBuilderTy builder(getContext()); builder.setInsertionPointAfter(op); + if (op.isIntegralComparison() && op.isStrongOrdering()) { + // For three-way comparisons on integral operands that produce strong + // ordering, we can generate potentially better code with the `llvm.scmp.*` + // and `llvm.ucmp.*` intrinsics. Thus we don't replace these comparisons + // here. They will be lowered directly to LLVMIR during the LLVM lowering + // pass. + // + // But we still need to take a step here. `llvm.scmp.*` and `llvm.ucmp.*` + // returns -1, 0, or 1 to represent lt, eq, and gt, which are the + // "canonicalized" result values of three-way comparisons. However, + // `cir.cmp3way` may not produce canonicalized result. We need to + // canonicalize the comparison if necessary. This is what we're doing in + // this special branch. + canonicalizeIntrinsicThreeWayCmp(builder, op); + return; + } + auto loc = op->getLoc(); auto cmpInfo = op.getInfo(); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index ad0f065993e0..647d2372196a 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -57,6 +57,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/Support/Casting.h" @@ -2057,6 +2058,16 @@ class CIRCmpOpLowering : public mlir::OpConversionPattern { } }; +static mlir::LLVM::CallIntrinsicOp +createCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter, + mlir::Location loc, const llvm::Twine &intrinsicName, + mlir::Type resultTy, mlir::ValueRange operands) { + auto intrinsicNameAttr = + mlir::StringAttr::get(rewriter.getContext(), intrinsicName); + return rewriter.create( + loc, resultTy, intrinsicNameAttr, operands); +} + static mlir::Value createLLVMBitOp(mlir::Location loc, const llvm::Twine &llvmIntrinBaseName, mlir::Type resultTy, mlir::Value operand, @@ -2069,8 +2080,6 @@ static mlir::Value createLLVMBitOp(mlir::Location loc, llvmIntrinBaseName.concat(".i") .concat(std::to_string(operandIntTy.getWidth())) .str(); - auto llvmIntrinNameAttr = - mlir::StringAttr::get(rewriter.getContext(), llvmIntrinName); // Note that LLVM intrinsic calls to bit intrinsics have the same type as the // operand. @@ -2078,12 +2087,12 @@ static mlir::Value createLLVMBitOp(mlir::Location loc, if (poisonZeroInputFlag.has_value()) { auto poisonZeroInputValue = rewriter.create( loc, rewriter.getI1Type(), static_cast(*poisonZeroInputFlag)); - op = rewriter.create( - loc, operand.getType(), llvmIntrinNameAttr, - mlir::ValueRange{operand, poisonZeroInputValue}); + op = createCallLLVMIntrinsicOp(rewriter, loc, llvmIntrinName, + operand.getType(), + {operand, poisonZeroInputValue}); } else { - op = rewriter.create( - loc, operand.getType(), llvmIntrinNameAttr, operand); + op = createCallLLVMIntrinsicOp(rewriter, loc, llvmIntrinName, + operand.getType(), operand); } mlir::Value result = op->getResult(0); @@ -2902,6 +2911,65 @@ class CIRIsConstantOpLowering } }; +class CIRCmpThreeWayOpLowering + : public mlir::OpConversionPattern { +public: + using mlir::OpConversionPattern< + mlir::cir::CmpThreeWayOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(mlir::cir::CmpThreeWayOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + assert(op.isIntegralComparison() && op.isStrongOrdering()); + + auto cmpInfo = op.getInfo(); + assert(cmpInfo.getLt() == -1 && cmpInfo.getEq() == 0 && + cmpInfo.getGt() == 1); + + auto operandTy = op.getLhs().getType().cast(); + auto resultTy = op.getType(); + auto llvmIntrinsicName = getLLVMIntrinsicName( + operandTy.isSigned(), operandTy.getWidth(), resultTy.getWidth()); + + rewriter.setInsertionPoint(op); + + auto llvmLhs = adaptor.getLhs(); + auto llvmRhs = adaptor.getRhs(); + auto llvmResultTy = getTypeConverter()->convertType(resultTy); + auto callIntrinsicOp = + createCallLLVMIntrinsicOp(rewriter, op.getLoc(), llvmIntrinsicName, + llvmResultTy, {llvmLhs, llvmRhs}); + + rewriter.replaceOp(op, callIntrinsicOp); + return mlir::success(); + } + +private: + static std::string getLLVMIntrinsicName(bool signedCmp, unsigned operandWidth, + unsigned resultWidth) { + // The intrinsic's name takes the form: + // `llvm..i.i` + + std::string result = "llvm."; + + if (signedCmp) + result.append("scmp."); + else + result.append("ucmp."); + + // Result type part. + result.push_back('i'); + result.append(std::to_string(resultWidth)); + result.push_back('.'); + + // Operand type part. + result.push_back('i'); + result.append(std::to_string(operandWidth)); + + return result; + } +}; + void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, mlir::TypeConverter &converter) { patterns.add(patterns.getContext()); @@ -2923,8 +2991,8 @@ void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, CIRVectorShuffleVecLowering, CIRStackSaveLowering, CIRStackRestoreLowering, CIRUnreachableLowering, CIRTrapLowering, CIRInlineAsmOpLowering, CIRSetBitfieldLowering, CIRGetBitfieldLowering, - CIRPrefetchLowering, CIRObjSizeOpLowering, CIRIsConstantOpLowering>( - converter, patterns.getContext()); + CIRPrefetchLowering, CIRObjSizeOpLowering, CIRIsConstantOpLowering, + CIRCmpThreeWayOpLowering>(converter, patterns.getContext()); } namespace { diff --git a/clang/test/CIR/CodeGen/Inputs/std-compare-noncanonical.h b/clang/test/CIR/CodeGen/Inputs/std-compare-noncanonical.h new file mode 100644 index 000000000000..e9bf110fa8a9 --- /dev/null +++ b/clang/test/CIR/CodeGen/Inputs/std-compare-noncanonical.h @@ -0,0 +1,110 @@ +#ifndef STD_COMPARE_H +#define STD_COMPARE_H + +namespace std { +inline namespace __1 { + +// exposition only +enum class _EqResult : unsigned char { + __equal = 2, + __equiv = __equal, +}; + +enum class _OrdResult : signed char { + __less = 1, + __greater = 3 +}; + +struct _CmpUnspecifiedType; +using _CmpUnspecifiedParam = void (_CmpUnspecifiedType::*)(); + +class strong_ordering { + using _ValueT = signed char; + explicit constexpr strong_ordering(_EqResult __v) noexcept : __value_(static_cast(__v)) {} + explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast(__v)) {} + +public: + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + static const strong_ordering greater; + + // comparisons + friend constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + friend constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(strong_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr strong_ordering strong_ordering::less(_OrdResult::__less); +inline constexpr strong_ordering strong_ordering::equal(_EqResult::__equal); +inline constexpr strong_ordering strong_ordering::equivalent(_EqResult::__equiv); +inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater); + +constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ == 0; +} +constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ != 0; +} +constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ < 0; +} +constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ <= 0; +} +constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ > 0; +} +constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 == __v.__value_; +} +constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 != __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 >= __v.__value_; +} + +constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return __v < 0 ? strong_ordering::greater : (__v > 0 ? strong_ordering::less : __v); +} + +} // namespace __1 +} // end namespace std + +#endif // STD_COMPARE_H diff --git a/clang/test/CIR/CodeGen/three-way-comparison-noncanonical.cpp b/clang/test/CIR/CodeGen/three-way-comparison-noncanonical.cpp new file mode 100644 index 000000000000..8feeeb10bfab --- /dev/null +++ b/clang/test/CIR/CodeGen/three-way-comparison-noncanonical.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir-enable -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=BEFORE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir-enable -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER + +#include "Inputs/std-compare-noncanonical.h" + +auto three_way_strong(int x, int y) { + return x <=> y; +} + +// BEFORE: #cmp3way_info_strong_lt1eq2gt3_ = #cir.cmp3way_info +// BEFORE: cir.func @_Z16three_way_strongii +// BEFORE: %{{.+}} = cir.cmp3way(%{{.+}} : !s32i, %{{.+}}, #cmp3way_info_strong_lt1eq2gt3_) : !s8i +// BEFORE: } + +// AFTER: #cmp3way_info_strong_ltn1eq0gt1_ = #cir.cmp3way_info +// AFTER: cir.func @_Z16three_way_strongii +// AFTER: %[[#CMP3WAY_RESULT:]] = cir.cmp3way(%{{.+}} : !s32i, %{{.+}}, #cmp3way_info_strong_ltn1eq0gt1_) : !s8i +// AFTER-NEXT: %[[#NEGONE:]] = cir.const(#cir.int<-1> : !s8i) : !s8i +// AFTER-NEXT: %[[#ONE:]] = cir.const(#cir.int<1> : !s8i) : !s8i +// AFTER-NEXT: %[[#CMP_TO_NEGONE:]] = cir.cmp(eq, %[[#CMP3WAY_RESULT]], %[[#NEGONE]]) : !s8i, !cir.bool +// AFTER-NEXT: %[[#A:]] = cir.ternary(%[[#CMP_TO_NEGONE]], true { +// AFTER-NEXT: cir.yield %[[#ONE]] : !s8i +// AFTER-NEXT: }, false { +// AFTER-NEXT: cir.yield %[[#CMP3WAY_RESULT]] : !s8i +// AFTER-NEXT: }) : (!cir.bool) -> !s8i +// AFTER-NEXT: %[[#ZERO:]] = cir.const(#cir.int<0> : !s8i) : !s8i +// AFTER-NEXT: %[[#TWO:]] = cir.const(#cir.int<2> : !s8i) : !s8i +// AFTER-NEXT: %[[#CMP_TO_ZERO:]] = cir.cmp(eq, %[[#A]], %[[#ZERO]]) : !s8i, !cir.bool +// AFTER-NEXT: %[[#B:]] = cir.ternary(%[[#CMP_TO_ZERO]], true { +// AFTER-NEXT: cir.yield %[[#TWO]] : !s8i +// AFTER-NEXT: }, false { +// AFTER-NEXT: cir.yield %[[#A]] : !s8i +// AFTER-NEXT: }) : (!cir.bool) -> !s8i +// AFTER-NEXT: %[[#ONE2:]] = cir.const(#cir.int<1> : !s8i) : !s8i +// AFTER-NEXT: %[[#THREE:]] = cir.const(#cir.int<3> : !s8i) : !s8i +// AFTER-NEXT: %[[#CMP_TO_ONE:]] = cir.cmp(eq, %[[#B]], %[[#ONE2]]) : !s8i, !cir.bool +// AFTER-NEXT: %{{.+}} = cir.ternary(%[[#CMP_TO_ONE]], true { +// AFTER-NEXT: cir.yield %[[#THREE]] : !s8i +// AFTER-NEXT: }, false { +// AFTER-NEXT: cir.yield %[[#B]] : !s8i +// AFTER-NEXT: }) : (!cir.bool) -> !s8i +// AFTER: } diff --git a/clang/test/CIR/CodeGen/three-way-comparison.cpp b/clang/test/CIR/CodeGen/three-way-comparison.cpp index 064aac274c61..bd8b67d864a2 100644 --- a/clang/test/CIR/CodeGen/three-way-comparison.cpp +++ b/clang/test/CIR/CodeGen/three-way-comparison.cpp @@ -16,25 +16,9 @@ auto three_way_strong(int x, int y) { // BEFORE: %{{.+}} = cir.cmp3way(%{{.+}} : !s32i, %{{.+}}, #cmp3way_info_strong_ltn1eq0gt1_) : !s8i // BEFORE: } -// AFTER: cir.func @_Z16three_way_strongii -// AFTER: %[[#LHS:]] = cir.load %{{.+}} : cir.ptr , !s32i -// AFTER-NEXT: %[[#RHS:]] = cir.load %{{.+}} : cir.ptr , !s32i -// AFTER-NEXT: %[[#LT:]] = cir.const(#cir.int<-1> : !s8i) : !s8i -// AFTER-NEXT: %[[#EQ:]] = cir.const(#cir.int<0> : !s8i) : !s8i -// AFTER-NEXT: %[[#GT:]] = cir.const(#cir.int<1> : !s8i) : !s8i -// AFTER-NEXT: %[[#CMP_LT:]] = cir.cmp(lt, %[[#LHS]], %[[#RHS]]) : !s32i, !cir.bool -// AFTER-NEXT: %[[#CMP_EQ:]] = cir.cmp(eq, %[[#LHS]], %[[#RHS]]) : !s32i, !cir.bool -// AFTER-NEXT: %[[#CMP_EQ_RES:]] = cir.ternary(%[[#CMP_EQ]], true { -// AFTER-NEXT: cir.yield %[[#EQ]] : !s8i -// AFTER-NEXT: }, false { -// AFTER-NEXT: cir.yield %[[#GT]] : !s8i -// AFTER-NEXT: }) : (!cir.bool) -> !s8i -// AFTER-NEXT: %{{.+}} = cir.ternary(%[[#CMP_LT]], true { -// AFTER-NEXT: cir.yield %[[#LT]] : !s8i -// AFTER-NEXT: }, false { -// AFTER-NEXT: cir.yield %[[#CMP_EQ_RES]] : !s8i -// AFTER-NEXT: }) : (!cir.bool) -> !s8i -// AFTER: } +// AFTER: cir.func @_Z16three_way_strongii +// AFTER: %{{.+}} = cir.cmp3way(%{{.+}} : !s32i, %{{.+}}, #cmp3way_info_strong_ltn1eq0gt1_) : !s8i +// AFTER: } auto three_way_weak(float x, float y) { return x <=> y; diff --git a/clang/test/CIR/Lowering/cmp3way.cir b/clang/test/CIR/Lowering/cmp3way.cir new file mode 100644 index 000000000000..6e00a9440f59 --- /dev/null +++ b/clang/test/CIR/Lowering/cmp3way.cir @@ -0,0 +1,40 @@ +// RUN: cir-opt %s -cir-to-llvm -o - | FileCheck %s -check-prefix=MLIR +// RUN: cir-translate %s -cir-to-llvmir | FileCheck %s -check-prefix=LLVM + +!s8i = !cir.int +!s32i = !cir.int +!u32i = !cir.int + +#cmp3way_info = #cir.cmp3way_info + +module { + cir.func @test_scmp(%arg0 : !s32i, %arg1 : !s32i) -> !s8i { + %0 = cir.cmp3way(%arg0 : !s32i, %arg1, #cmp3way_info) : !s8i + cir.return %0 : !s8i + } + + // MLIR: llvm.func @test_scmp(%arg0: i32, %arg1: i32) -> i8 + // MLIR-NEXT: %0 = llvm.call_intrinsic "llvm.scmp.i8.i32"(%arg0, %arg1) : (i32, i32) -> i8 + // MLIR-NEXT: llvm.return %0 : i8 + // MLIR-NEXT: } + + // LLVM: define i8 @test_scmp(i32 %0, i32 %1) + // LLVM-NEXT: %[[#RET:]] = call i8 @llvm.scmp.i8.i32(i32 %0, i32 %1) + // LLVM-NEXT: ret i8 %[[#RET]] + // LLVM-NEXT: } + + cir.func @test_ucmp(%arg0 : !u32i, %arg1 : !u32i) -> !s8i { + %0 = cir.cmp3way(%arg0 : !u32i, %arg1, #cmp3way_info) : !s8i + cir.return %0 : !s8i + } + + // MLIR: llvm.func @test_ucmp(%arg0: i32, %arg1: i32) -> i8 + // MLIR-NEXT: %0 = llvm.call_intrinsic "llvm.ucmp.i8.i32"(%arg0, %arg1) : (i32, i32) -> i8 + // MLIR-NEXT: llvm.return %0 : i8 + // MLIR-NEXT: } + + // LLVM: define i8 @test_ucmp(i32 %0, i32 %1) + // LLVM-NEXT: %[[#RET:]] = call i8 @llvm.ucmp.i8.i32(i32 %0, i32 %1) + // LLVM-NEXT: ret i8 %[[#RET]] + // LLVM-NEXT: } +}