https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/133159
>From 6db5880fa7cdf6363d7e0025f811f42ec273df52 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Wed, 26 Mar 2025 20:50:57 +0000 Subject: [PATCH 1/4] [CIR] Upstream CmpOp This patch adds support for comparison operators with ClangIR, both integral and floating point. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 5 + clang/include/clang/CIR/Dialect/IR/CIROps.td | 44 ++++ clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 83 +++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 94 ++++++- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 14 ++ clang/test/CIR/CodeGen/cmp.cpp | 233 ++++++++++++++++++ 7 files changed, 471 insertions(+), 3 deletions(-) create mode 100644 clang/test/CIR/CodeGen/cmp.cpp diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index ac7658276ec37..b94e9f8490be5 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -265,6 +265,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createAdd(loc, lhs, rhs, OverflowBehavior::NoUnsignedWrap); } + cir::CmpOp createCompare(mlir::Location loc, cir::CmpOpKind kind, + mlir::Value lhs, mlir::Value rhs) { + return create<cir::CmpOp>(loc, getBoolTy(), kind, lhs, rhs); + } + // // Block handling helpers // ---------------------- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 455cc2b8b0277..3fba7566e9f1b 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -826,6 +826,50 @@ def ForOp : CIR_Op<"for", [LoopOpInterface, NoRegionArguments]> { }]; } +//===----------------------------------------------------------------------===// +// CmpOp +//===----------------------------------------------------------------------===// + +def CmpOpKind_LT : I32EnumAttrCase<"lt", 1>; +def CmpOpKind_LE : I32EnumAttrCase<"le", 2>; +def CmpOpKind_GT : I32EnumAttrCase<"gt", 3>; +def CmpOpKind_GE : I32EnumAttrCase<"ge", 4>; +def CmpOpKind_EQ : I32EnumAttrCase<"eq", 5>; +def CmpOpKind_NE : I32EnumAttrCase<"ne", 6>; + +def CmpOpKind : I32EnumAttr< + "CmpOpKind", + "compare operation kind", + [CmpOpKind_LT, CmpOpKind_LE, CmpOpKind_GT, + CmpOpKind_GE, CmpOpKind_EQ, CmpOpKind_NE]> { + let cppNamespace = "::cir"; +} + +def CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> { + + let summary = "Compare values two values and produce a boolean result"; + let description = [{ + `cir.cmp` compares two input operands of the same type and produces a + `cir.bool` result. The kinds of comparison available are: + [lt,gt,ge,eq,ne] + + ```mlir + %7 = cir.cmp(gt, %1, %2) : i32, !cir.bool + ``` + }]; + + let results = (outs CIR_BoolType:$result); + let arguments = (ins Arg<CmpOpKind, "cmp kind">:$kind, + CIR_AnyType:$lhs, CIR_AnyType:$rhs); + + let assemblyFormat = [{ + `(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) `,` type($result) attr-dict + }]; + + // Already covered by the traits + let hasVerifier = 0; +} + //===----------------------------------------------------------------------===// // BinOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 3a102d90aba8f..6e93f50ac83e6 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -89,7 +89,6 @@ struct MissingFeatures { static bool opGlobalViewAttr() { return false; } static bool lowerModeOptLevel() { return false; } static bool opTBAA() { return false; } - static bool opCmp() { return false; } static bool objCLifetime() { return false; } static bool emitNullabilityCheck() { return false; } static bool astVarDeclInterface() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 52bd3b2933744..992c92635369d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -710,6 +710,89 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { HANDLEBINOP(Xor) HANDLEBINOP(Or) #undef HANDLEBINOP + + mlir::Value emitCmp(const BinaryOperator *e) { + mlir::Value result; + QualType lhsTy = e->getLHS()->getType(); + QualType rhsTy = e->getRHS()->getType(); + + auto clangCmpToCIRCmp = + [](clang::BinaryOperatorKind clangCmp) -> cir::CmpOpKind { + switch (clangCmp) { + case BO_LT: + return cir::CmpOpKind::lt; + case BO_GT: + return cir::CmpOpKind::gt; + case BO_LE: + return cir::CmpOpKind::le; + case BO_GE: + return cir::CmpOpKind::ge; + case BO_EQ: + return cir::CmpOpKind::eq; + case BO_NE: + return cir::CmpOpKind::ne; + default: + llvm_unreachable("unsupported comparison kind"); + } + }; + + if (lhsTy->getAs<MemberPointerType>()) { + assert(e->getOpcode() == BO_EQ || e->getOpcode() == BO_NE); + mlir::Value lhs = cgf.emitScalarExpr(e->getLHS()); + mlir::Value rhs = cgf.emitScalarExpr(e->getRHS()); + cir::CmpOpKind kind = clangCmpToCIRCmp(e->getOpcode()); + result = + builder.createCompare(cgf.getLoc(e->getExprLoc()), kind, lhs, rhs); + } else if (!lhsTy->isAnyComplexType() && !rhsTy->isAnyComplexType()) { + BinOpInfo boInfo = emitBinOps(e); + mlir::Value lhs = boInfo.lhs; + mlir::Value rhs = boInfo.rhs; + + if (lhsTy->isVectorType()) { + assert(!cir::MissingFeatures::vectorType()); + cgf.cgm.errorNYI(boInfo.loc, "vector comparisons"); + result = builder.getBool(false, cgf.getLoc(boInfo.loc)); + } else if (boInfo.isFixedPointOp()) { + assert(!cir::MissingFeatures::fixedPointType()); + cgf.cgm.errorNYI(boInfo.loc, "fixed point comparisons"); + result = builder.getBool(false, cgf.getLoc(boInfo.loc)); + } else if (lhsTy->hasSignedIntegerRepresentation()) { + cir::CmpOpKind kind = clangCmpToCIRCmp(e->getOpcode()); + result = builder.createCompare(cgf.getLoc(boInfo.loc), kind, lhs, rhs); + } else { + // Unsigned integers and pointers. + if (cgf.cgm.getCodeGenOpts().StrictVTablePointers && + mlir::isa<cir::PointerType>(lhs.getType()) && + mlir::isa<cir::PointerType>(rhs.getType())) { + cgf.cgm.errorNYI(boInfo.loc, "strict vtable pointer comparisons"); + result = builder.getBool(false, cgf.getLoc(boInfo.loc)); + } + + cir::CmpOpKind kind = clangCmpToCIRCmp(e->getOpcode()); + result = builder.createCompare(cgf.getLoc(boInfo.loc), kind, lhs, rhs); + } + } else { + // Complex Comparison: can only be an equality comparison. + assert(!cir::MissingFeatures::complexType()); + const mlir::Location loc = cgf.getLoc(e->getSourceRange()); + cgf.cgm.errorNYI(loc, "complex comparison"); + result = builder.getBool(false, loc); + } + + return emitScalarConversion(result, cgf.getContext().BoolTy, e->getType(), + e->getExprLoc()); + } + +// Comparisons. +#define VISITCOMP(CODE) \ + mlir::Value VisitBin##CODE(const BinaryOperator *E) { return emitCmp(E); } + VISITCOMP(LT) + VISITCOMP(GT) + VISITCOMP(LE) + VISITCOMP(GE) + VISITCOMP(EQ) + VISITCOMP(NE) +#undef VISITCOMP }; LValue ScalarExprEmitter::emitCompoundAssignLValue( diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 1c2b9ad05a132..f58e1cb21bb49 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -20,6 +20,7 @@ #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/BuiltinDialect.h" #include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Types.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" #include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h" @@ -514,9 +515,17 @@ mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( assert(!MissingFeatures::cxxABI()); assert(!MissingFeatures::dataMemberType()); break; - case cir::CastKind::ptr_to_bool: - assert(!cir::MissingFeatures::opCmp()); + case cir::CastKind::ptr_to_bool: { + auto zero = + mlir::IntegerAttr::get(mlir::IntegerType::get(getContext(), 64), 0); + auto null = rewriter.create<cir::ConstantOp>( + castOp.getLoc(), castOp.getSrc().getType(), + cir::ConstPtrAttr::get(getContext(), castOp.getSrc().getType(), zero)); + rewriter.replaceOpWithNewOp<cir::CmpOp>( + castOp, cir::BoolType::get(getContext()), cir::CmpOpKind::ne, + castOp.getSrc(), null); break; + } case cir::CastKind::address_space: { mlir::Type dstTy = castOp.getType(); mlir::Value llvmSrcVal = adaptor.getOperands().front(); @@ -1117,6 +1126,86 @@ mlir::LogicalResult CIRToLLVMBinOpLowering::matchAndRewrite( return mlir::LogicalResult::success(); } +/// Convert from a CIR comparison kind to an LLVM IR integral comparison kind. +static mlir::LLVM::ICmpPredicate +convertCmpKindToICmpPredicate(cir::CmpOpKind kind, bool isSigned) { + using CIR = cir::CmpOpKind; + using LLVMICmp = mlir::LLVM::ICmpPredicate; + switch (kind) { + case CIR::eq: + return LLVMICmp::eq; + case CIR::ne: + return LLVMICmp::ne; + case CIR::lt: + return (isSigned ? LLVMICmp::slt : LLVMICmp::ult); + case CIR::le: + return (isSigned ? LLVMICmp::sle : LLVMICmp::ule); + case CIR::gt: + return (isSigned ? LLVMICmp::sgt : LLVMICmp::ugt); + case CIR::ge: + return (isSigned ? LLVMICmp::sge : LLVMICmp::uge); + } + llvm_unreachable("Unknown CmpOpKind"); +} + +/// Convert from a CIR comparison kind to an LLVM IR floating-point comparison +/// kind. +static mlir::LLVM::FCmpPredicate +convertCmpKindToFCmpPredicate(cir::CmpOpKind kind) { + using CIR = cir::CmpOpKind; + using LLVMFCmp = mlir::LLVM::FCmpPredicate; + switch (kind) { + case CIR::eq: + return LLVMFCmp::oeq; + case CIR::ne: + return LLVMFCmp::une; + case CIR::lt: + return LLVMFCmp::olt; + case CIR::le: + return LLVMFCmp::ole; + case CIR::gt: + return LLVMFCmp::ogt; + case CIR::ge: + return LLVMFCmp::oge; + } + llvm_unreachable("Unknown CmpOpKind"); +} + +mlir::LogicalResult CIRToLLVMCmpOpLowering::matchAndRewrite( + cir::CmpOp cmpOp, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type type = cmpOp.getLhs().getType(); + + assert(!cir::MissingFeatures::dataMemberType()); + assert(!cir::MissingFeatures::methodType()); + + // Lower to LLVM comparison op. + if (mlir::isa<cir::IntType, mlir::IntegerType>(type)) { + bool isSigned = mlir::isa<cir::IntType>(type) + ? mlir::cast<cir::IntType>(type).isSigned() + : mlir::cast<mlir::IntegerType>(type).isSigned(); + mlir::LLVM::ICmpPredicate kind = + convertCmpKindToICmpPredicate(cmpOp.getKind(), isSigned); + rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( + cmpOp, kind, adaptor.getLhs(), adaptor.getRhs()); + } else if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(type)) { + mlir::LLVM::ICmpPredicate kind = + convertCmpKindToICmpPredicate(cmpOp.getKind(), + /* isSigned=*/false); + rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( + cmpOp, kind, adaptor.getLhs(), adaptor.getRhs()); + } else if (mlir::isa<cir::CIRFPTypeInterface>(type)) { + mlir::LLVM::FCmpPredicate kind = + convertCmpKindToFCmpPredicate(cmpOp.getKind()); + rewriter.replaceOpWithNewOp<mlir::LLVM::FCmpOp>( + cmpOp, kind, adaptor.getLhs(), adaptor.getRhs()); + } else { + return cmpOp.emitError() << "unsupported type for CmpOp: " << type; + } + + return mlir::success(); +} + static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, mlir::DataLayout &dataLayout) { converter.addConversion([&](cir::PointerType type) -> mlir::Type { @@ -1258,6 +1347,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { CIRToLLVMBinOpLowering, CIRToLLVMBrCondOpLowering, CIRToLLVMBrOpLowering, + CIRToLLVMCmpOpLowering, CIRToLLVMFuncOpLowering, CIRToLLVMTrapOpLowering, CIRToLLVMUnaryOpLowering diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index ef0bb2deaccdf..c57f86f57ecde 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -189,6 +189,20 @@ class CIRToLLVMBinOpLowering : public mlir::OpConversionPattern<cir::BinOp> { mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMCmpOpLowering : public mlir::OpConversionPattern<cir::CmpOp> { +public: + CIRToLLVMCmpOpLowering(const mlir::TypeConverter &typeConverter, + mlir::MLIRContext *context) + : OpConversionPattern(typeConverter, context) { + setHasBoundedRewriteRecursion(); + } + + mlir::LogicalResult + matchAndRewrite(cir::CmpOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + + class CIRToLLVMBrOpLowering : public mlir::OpConversionPattern<cir::BrOp> { public: using mlir::OpConversionPattern<cir::BrOp>::OpConversionPattern; diff --git a/clang/test/CIR/CodeGen/cmp.cpp b/clang/test/CIR/CodeGen/cmp.cpp new file mode 100644 index 0000000000000..57d6b5b411f98 --- /dev/null +++ b/clang/test/CIR/CodeGen/cmp.cpp @@ -0,0 +1,233 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -DCIR_ONLY %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM + +void c0(int a, int b) { + bool x = a > b; + x = a < b; + x = a <= b; + x = a >= b; + x = a != b; + x = a == b; +} + +// CIR: cir.func @c0( + +// CIR: %[[A_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init] +// CIR: %[[X_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init] + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] +// CIR: %{{.*}} = cir.cmp(gt, %[[A1]], %[[B1]]) : !s32i, !cir.bool +// CIR: cir.store {{.*}}, %[[X_PTR]] + +// CIR: %[[A2:.*]] = cir.load %[[A_PTR]] +// CIR: %[[B2:.*]] = cir.load %[[B_PTR]] +// CIR: %{{.*}} = cir.cmp(lt, %[[A2]], %[[B2]]) : !s32i, !cir.bool + +// CIR: %[[A3:.*]] = cir.load %[[A_PTR]] +// CIR: %[[B3:.*]] = cir.load %[[B_PTR]] +// CIR: %{{.*}} = cir.cmp(le, %[[A3]], %[[B3]]) : !s32i, !cir.bool + +// CIR: %[[A4:.*]] = cir.load %[[A_PTR]] +// CIR: %[[B4:.*]] = cir.load %[[B_PTR]] +// CIR: %{{.*}} = cir.cmp(ge, %[[A4]], %[[B4]]) : !s32i, !cir.bool + +// CIR: %[[A5:.*]] = cir.load %[[A_PTR]] +// CIR: %[[B5:.*]] = cir.load %[[B_PTR]] +// CIR: %{{.*}} = cir.cmp(ne, %[[A5]], %[[B5]]) : !s32i, !cir.bool + +// CIR: %[[A6:.*]] = cir.load %[[A_PTR]] +// CIR: %[[B6:.*]] = cir.load %[[B_PTR]] +// CIR: %{{.*}} = cir.cmp(eq, %[[A6]], %[[B6]]) : !s32i, !cir.bool + +// LLVM: define void @c0(i32 %0, i32 %1) { +// LLVM: %[[PTR1:.*]] = alloca i32, i64 1 +// LLVM: %[[PTR2:.*]] = alloca i32, i64 1 +// LLVM: %[[BOOL_PTR:.*]] = alloca i8, i64 1 +// LLVM: store i32 %0, ptr %[[PTR1]] +// LLVM: store i32 %1, ptr %[[PTR2]] + +// LLVM: %[[A1:.*]] = load i32, ptr %[[PTR1]] +// LLVM: %[[B1:.*]] = load i32, ptr %[[PTR2]] +// LLVM: %[[CMP1:.*]] = icmp sgt i32 %[[A1]], %[[B1]] +// LLVM: %[[ZEXT1:.*]] = zext i1 %[[CMP1]] to i8 +// LLVM: store i8 %[[ZEXT1]], ptr %[[BOOL_PTR]] + +// LLVM: %[[A2:.*]] = load i32, ptr %[[PTR1]] +// LLVM: %[[B2:.*]] = load i32, ptr %[[PTR2]] +// LLVM: %[[CMP2:.*]] = icmp slt i32 %[[A2]], %[[B2]] +// LLVM: %[[ZEXT2:.*]] = zext i1 %[[CMP2]] to i8 +// LLVM: store i8 %[[ZEXT2]], ptr %[[BOOL_PTR]] + +// LLVM: %[[A3:.*]] = load i32, ptr %[[PTR1]] +// LLVM: %[[B3:.*]] = load i32, ptr %[[PTR2]] +// LLVM: %[[CMP3:.*]] = icmp sle i32 %[[A3]], %[[B3]] +// LLVM: %[[ZEXT3:.*]] = zext i1 %[[CMP3]] to i8 +// LLVM: store i8 %[[ZEXT3]], ptr %[[BOOL_PTR]] + +// LLVM: %[[A4:.*]] = load i32, ptr %[[PTR1]] +// LLVM: %[[B4:.*]] = load i32, ptr %[[PTR2]] +// LLVM: %[[CMP4:.*]] = icmp sge i32 %[[A4]], %[[B4]] +// LLVM: %[[ZEXT4:.*]] = zext i1 %[[CMP4]] to i8 +// LLVM: store i8 %[[ZEXT4]], ptr %[[BOOL_PTR]] + +// LLVM: %[[A5:.*]] = load i32, ptr %[[PTR1]] +// LLVM: %[[B5:.*]] = load i32, ptr %[[PTR2]] +// LLVM: %[[CMP5:.*]] = icmp ne i32 %[[A5]], %[[B5]] +// LLVM: %[[ZEXT5:.*]] = zext i1 %[[CMP5]] to i8 +// LLVM: store i8 %[[ZEXT5]], ptr %[[BOOL_PTR]] + +// LLVM: %[[A6:.*]] = load i32, ptr %[[PTR1]] +// LLVM: %[[B6:.*]] = load i32, ptr %[[PTR2]] +// LLVM: %[[CMP6:.*]] = icmp eq i32 %[[A6]], %[[B6]] +// LLVM: %[[ZEXT6:.*]] = zext i1 %[[CMP6]] to i8 +// LLVM: store i8 %[[ZEXT6]], ptr %[[BOOL_PTR]] + +void c0_unsigned(unsigned int a, unsigned int b) { + bool x = a > b; + x = a < b; + x = a <= b; + x = a >= b; + x = a != b; + x = a == b; +} + +// CIR: cir.func @c0_unsigned( + +// CIR: %[[U_A_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["a", init] +// CIR: %[[U_B_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["b", init] +// CIR: %[[U_X_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init] + +// CIR: %[[UA1:.*]] = cir.load %[[U_A_PTR]] +// CIR: %[[UB1:.*]] = cir.load %[[U_B_PTR]] +// CIR: %{{.*}} = cir.cmp(gt, %[[UA1]], %[[UB1]]) : !u32i, !cir.bool + +// CIR: %[[UA2:.*]] = cir.load %[[U_A_PTR]] +// CIR: %[[UB2:.*]] = cir.load %[[U_B_PTR]] +// CIR: %{{.*}} = cir.cmp(lt, %[[UA2]], %[[UB2]]) : !u32i, !cir.bool + +// CIR: %[[UA3:.*]] = cir.load %[[U_A_PTR]] +// CIR: %[[UB3:.*]] = cir.load %[[U_B_PTR]] +// CIR: %{{.*}} = cir.cmp(le, %[[UA3]], %[[UB3]]) : !u32i, !cir.bool + +// CIR: %[[UA4:.*]] = cir.load %[[U_A_PTR]] +// CIR: %[[UB4:.*]] = cir.load %[[U_B_PTR]] +// CIR: %{{.*}} = cir.cmp(ge, %[[UA4]], %[[UB4]]) : !u32i, !cir.bool + +// CIR: %[[UA5:.*]] = cir.load %[[U_A_PTR]] +// CIR: %[[UB5:.*]] = cir.load %[[U_B_PTR]] +// CIR: %{{.*}} = cir.cmp(ne, %[[UA5]], %[[UB5]]) : !u32i, !cir.bool + +// CIR: %[[UA6:.*]] = cir.load %[[U_A_PTR]] +// CIR: %[[UB6:.*]] = cir.load %[[U_B_PTR]] +// CIR: %{{.*}} = cir.cmp(eq, %[[UA6]], %[[UB6]]) : !u32i, !cir.bool + +// LLVM: define void @c0_unsigned(i32 %0, i32 %1) { +// LLVM: %[[U_PTR1:.*]] = alloca i32, i64 1 +// LLVM: %[[U_PTR2:.*]] = alloca i32, i64 1 +// LLVM: %[[U_BOOL_PTR:.*]] = alloca i8, i64 1 +// LLVM: store i32 %0, ptr %[[U_PTR1]] +// LLVM: store i32 %1, ptr %[[U_PTR2]] + +// LLVM: %[[UA1:.*]] = load i32, ptr %[[U_PTR1]] +// LLVM: %[[UB1:.*]] = load i32, ptr %[[U_PTR2]] +// LLVM: %[[UCMP1:.*]] = icmp ugt i32 %[[UA1]], %[[UB1]] +// LLVM: %[[UZEXT1:.*]] = zext i1 %[[UCMP1]] to i8 +// LLVM: store i8 %[[UZEXT1]], ptr %[[U_BOOL_PTR]] + +// LLVM: %[[UA2:.*]] = load i32, ptr %[[U_PTR1]] +// LLVM: %[[UB2:.*]] = load i32, ptr %[[U_PTR2]] +// LLVM: %[[UCMP2:.*]] = icmp ult i32 %[[UA2]], %[[UB2]] +// LLVM: %[[UZEXT2:.*]] = zext i1 %[[UCMP2]] to i8 +// LLVM: store i8 %[[UZEXT2]], ptr %[[U_BOOL_PTR]] + +// LLVM: %[[UA3:.*]] = load i32, ptr %[[U_PTR1]] +// LLVM: %[[UB3:.*]] = load i32, ptr %[[U_PTR2]] +// LLVM: %[[UCMP3:.*]] = icmp ule i32 %[[UA3]], %[[UB3]] +// LLVM: %[[UZEXT3:.*]] = zext i1 %[[UCMP3]] to i8 +// LLVM: store i8 %[[UZEXT3]], ptr %[[U_BOOL_PTR]] + +// LLVM: %[[UA4:.*]] = load i32, ptr %[[U_PTR1]] +// LLVM: %[[UB4:.*]] = load i32, ptr %[[U_PTR2]] +// LLVM: %[[UCMP4:.*]] = icmp uge i32 %[[UA4]], %[[UB4]] +// LLVM: %[[UZEXT4:.*]] = zext i1 %[[UCMP4]] to i8 +// LLVM: store i8 %[[UZEXT4]], ptr %[[U_BOOL_PTR]] + +// LLVM: %[[UA5:.*]] = load i32, ptr %[[U_PTR1]] +// LLVM: %[[UB5:.*]] = load i32, ptr %[[U_PTR2]] +// LLVM: %[[UCMP5:.*]] = icmp ne i32 %[[UA5]], %[[UB5]] +// LLVM: %[[UZEXT5:.*]] = zext i1 %[[UCMP5]] to i8 +// LLVM: store i8 %[[UZEXT5]], ptr %[[U_BOOL_PTR]] + +// LLVM: %[[UA6:.*]] = load i32, ptr %[[U_PTR1]] +// LLVM: %[[UB6:.*]] = load i32, ptr %[[U_PTR2]] +// LLVM: %[[UCMP6:.*]] = icmp eq i32 %[[UA6]], %[[UB6]] +// LLVM: %[[UZEXT6:.*]] = zext i1 %[[UCMP6]] to i8 +// LLVM: store i8 %[[UZEXT6]], ptr %[[U_BOOL_PTR]] + +void c0_float(float a, float b) { + bool x = a > b; + x = a < b; + x = a <= b; + x = a >= b; + x = a != b; + x = a == b; +} + +// CIR: cir.func @c0_float(%arg0: !cir.float{{.*}}, %arg1: !cir.float{{.*}}) { +// CIR: %[[A_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["b", init] +// CIR: %[[X_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init] + +// CIR: cir.store %arg0, %[[A_PTR]] : !cir.float, !cir.ptr<!cir.float> +// CIR: cir.store %arg1, %[[B_PTR]] : !cir.float, !cir.ptr<!cir.float> + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[CMP1:.*]] = cir.cmp(gt, %[[A1]], %[[B1]]) : !cir.float, !cir.bool +// CIR: cir.store %[[CMP1]], %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> + +// CIR: %[[A2:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[B2:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[CMP2:.*]] = cir.cmp(lt, %[[A2]], %[[B2]]) : !cir.float, !cir.bool +// CIR: cir.store %[[CMP2]], %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> + +// CIR: %[[A3:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[B3:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[CMP3:.*]] = cir.cmp(le, %[[A3]], %[[B3]]) : !cir.float, !cir.bool +// CIR: cir.store %[[CMP3]], %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> + +// CIR: %[[A4:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[B4:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[CMP4:.*]] = cir.cmp(ge, %[[A4]], %[[B4]]) : !cir.float, !cir.bool +// CIR: cir.store %[[CMP4]], %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> + +// CIR: %[[A5:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[B5:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[CMP5:.*]] = cir.cmp(ne, %[[A5]], %[[B5]]) : !cir.float, !cir.bool +// CIR: cir.store %[[CMP5]], %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> + +// CIR: %[[A6:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[B6:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[CMP6:.*]] = cir.cmp(eq, %[[A6]], %[[B6]]) : !cir.float, !cir.bool +// CIR: cir.store %[[CMP6]], %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> + +// LLVM: define void @c0_float(float %0, float %1) { +// LLVM: %[[A_PTR:.*]] = alloca float +// LLVM: %[[B_PTR:.*]] = alloca float +// LLVM: store float %0, ptr %[[A_PTR]] +// LLVM: store float %1, ptr %[[B_PTR]] + +// LLVM: load float, ptr %[[A_PTR]] +// LLVM: load float, ptr %[[B_PTR]] +// LLVM: fcmp ogt float %{{.*}}, %{{.*}} +// LLVM: zext i1 %{{.*}} to i8 + +// LLVM: fcmp olt float %{{.*}}, %{{.*}} +// LLVM: fcmp ole float %{{.*}}, %{{.*}} +// LLVM: fcmp oge float %{{.*}}, %{{.*}} +// LLVM: fcmp une float %{{.*}}, %{{.*}} +// LLVM: fcmp oeq float %{{.*}}, %{{.*}} >From 614c8fee42cf0df0de9face0ce870977359d3c9a Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Wed, 26 Mar 2025 20:59:04 +0000 Subject: [PATCH 2/4] clang-format --- clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index c57f86f57ecde..da28c1f22f96a 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -202,7 +202,6 @@ class CIRToLLVMCmpOpLowering : public mlir::OpConversionPattern<cir::CmpOp> { mlir::ConversionPatternRewriter &) const override; }; - class CIRToLLVMBrOpLowering : public mlir::OpConversionPattern<cir::BrOp> { public: using mlir::OpConversionPattern<cir::BrOp>::OpConversionPattern; >From f9a74a66e9404e1caa6431c7e44909c1e9697624 Mon Sep 17 00:00:00 2001 From: Morris Hafner <m...@users.noreply.github.com> Date: Thu, 27 Mar 2025 14:46:17 +0000 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Henrich Lauko <xla...@mail.muni.cz> Co-authored-by: Andy Kaylor <akay...@nvidia.com> --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 3fba7566e9f1b..b879fe10036a8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -865,9 +865,6 @@ def CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> { let assemblyFormat = [{ `(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) `,` type($result) attr-dict }]; - - // Already covered by the traits - let hasVerifier = 0; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 992c92635369d..b164b9a20796c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -732,7 +732,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { case BO_NE: return cir::CmpOpKind::ne; default: - llvm_unreachable("unsupported comparison kind"); + llvm_unreachable("unsupported comparison kind for cir.cmp"); } }; >From c38442e009a1c96fcde43b57ed7ffc18dfa7cb94 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Thu, 27 Mar 2025 17:09:04 +0000 Subject: [PATCH 4/4] Address review feedback - Expand testing --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 22 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 13 +- clang/test/CIR/CodeGen/cast.cpp | 23 +- clang/test/CIR/CodeGen/cmp.cpp | 251 +++++++++++++++++- 4 files changed, 273 insertions(+), 36 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index b164b9a20796c..ffbdf70da8e9c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -712,6 +712,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { #undef HANDLEBINOP mlir::Value emitCmp(const BinaryOperator *e) { + const mlir::Location loc = cgf.getLoc(e->getExprLoc()); mlir::Value result; QualType lhsTy = e->getLHS()->getType(); QualType rhsTy = e->getRHS()->getType(); @@ -737,12 +738,12 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { }; if (lhsTy->getAs<MemberPointerType>()) { + assert(!cir::MissingFeatures::dataMemberType()); assert(e->getOpcode() == BO_EQ || e->getOpcode() == BO_NE); mlir::Value lhs = cgf.emitScalarExpr(e->getLHS()); mlir::Value rhs = cgf.emitScalarExpr(e->getRHS()); cir::CmpOpKind kind = clangCmpToCIRCmp(e->getOpcode()); - result = - builder.createCompare(cgf.getLoc(e->getExprLoc()), kind, lhs, rhs); + result = builder.createCompare(loc, kind, lhs, rhs); } else if (!lhsTy->isAnyComplexType() && !rhsTy->isAnyComplexType()) { BinOpInfo boInfo = emitBinOps(e); mlir::Value lhs = boInfo.lhs; @@ -750,31 +751,26 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { if (lhsTy->isVectorType()) { assert(!cir::MissingFeatures::vectorType()); - cgf.cgm.errorNYI(boInfo.loc, "vector comparisons"); - result = builder.getBool(false, cgf.getLoc(boInfo.loc)); + cgf.cgm.errorNYI(loc, "vector comparisons"); + result = builder.getBool(false, loc); } else if (boInfo.isFixedPointOp()) { assert(!cir::MissingFeatures::fixedPointType()); - cgf.cgm.errorNYI(boInfo.loc, "fixed point comparisons"); - result = builder.getBool(false, cgf.getLoc(boInfo.loc)); - } else if (lhsTy->hasSignedIntegerRepresentation()) { - cir::CmpOpKind kind = clangCmpToCIRCmp(e->getOpcode()); - result = builder.createCompare(cgf.getLoc(boInfo.loc), kind, lhs, rhs); + cgf.cgm.errorNYI(loc, "fixed point comparisons"); + result = builder.getBool(false, loc); } else { // Unsigned integers and pointers. if (cgf.cgm.getCodeGenOpts().StrictVTablePointers && mlir::isa<cir::PointerType>(lhs.getType()) && mlir::isa<cir::PointerType>(rhs.getType())) { - cgf.cgm.errorNYI(boInfo.loc, "strict vtable pointer comparisons"); - result = builder.getBool(false, cgf.getLoc(boInfo.loc)); + cgf.cgm.errorNYI(loc, "strict vtable pointer comparisons"); } cir::CmpOpKind kind = clangCmpToCIRCmp(e->getOpcode()); - result = builder.createCompare(cgf.getLoc(boInfo.loc), kind, lhs, rhs); + result = builder.createCompare(loc, kind, lhs, rhs); } } else { // Complex Comparison: can only be an equality comparison. assert(!cir::MissingFeatures::complexType()); - const mlir::Location loc = cgf.getLoc(e->getSourceRange()); cgf.cgm.errorNYI(loc, "complex comparison"); result = builder.getBool(false, loc); } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index f58e1cb21bb49..d044cf8923fed 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -516,14 +516,11 @@ mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( assert(!MissingFeatures::dataMemberType()); break; case cir::CastKind::ptr_to_bool: { - auto zero = - mlir::IntegerAttr::get(mlir::IntegerType::get(getContext(), 64), 0); - auto null = rewriter.create<cir::ConstantOp>( - castOp.getLoc(), castOp.getSrc().getType(), - cir::ConstPtrAttr::get(getContext(), castOp.getSrc().getType(), zero)); - rewriter.replaceOpWithNewOp<cir::CmpOp>( - castOp, cir::BoolType::get(getContext()), cir::CmpOpKind::ne, - castOp.getSrc(), null); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Value zeroPtr = rewriter.create<mlir::LLVM::ZeroOp>( + castOp.getLoc(), llvmSrcVal.getType()); + rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( + castOp, mlir::LLVM::ICmpPredicate::ne, llvmSrcVal, zeroPtr); break; } case cir::CastKind::address_space: { diff --git a/clang/test/CIR/CodeGen/cast.cpp b/clang/test/CIR/CodeGen/cast.cpp index ceae355a3ae1c..4a880e3479ea2 100644 --- a/clang/test/CIR/CodeGen/cast.cpp +++ b/clang/test/CIR/CodeGen/cast.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -DCIR_ONLY %s -o %t.cir +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll // RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM @@ -57,16 +57,16 @@ int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { // CIR: %{{[0-9]+}} = cir.cast(bool_to_int, %{{[0-9]+}} : !cir.bool), !s32i // LLVM: %{{[0-9]+}} = zext i1 %{{[0-9]+}} to i32 - #ifdef CIR_ONLY bool b2 = x2; // int to bool // CIR: %{{[0-9]+}} = cir.cast(int_to_bool, %{{[0-9]+}} : !s32i), !cir.bool - #endif + // LLVM: %[[INTTOBOOL:[0-9]+]] = icmp ne i32 %{{[0-9]+}}, 0 + // LLVM: zext i1 %[[INTTOBOOL]] to i8 - #ifdef CIR_ONLY void *p; - bool b3 = p; // ptr to bool + bool b3 = p; // ptr to bool // CIR: %{{[0-9]+}} = cir.cast(ptr_to_bool, %{{[0-9]+}} : !cir.ptr<!void>), !cir.bool - #endif + // LLVM: %[[PTRTOBOOL:[0-9]+]] = icmp ne ptr %{{[0-9]+}}, null + // LLVM: zext i1 %[[PTRTOBOOL]] to i8 float f; bool b4 = f; // float to bool @@ -77,7 +77,6 @@ int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { return 0; } -#ifdef CIR_ONLY bool cptr(void *d) { bool x = d; return x; @@ -88,7 +87,15 @@ bool cptr(void *d) { // CIR: %[[DVAL:[0-9]+]] = cir.load %[[DPTR]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> // CIR: %{{[0-9]+}} = cir.cast(ptr_to_bool, %[[DVAL]] : !cir.ptr<!void>), !cir.bool -#endif + +// LLVM-LABEL: define i1 @cptr(ptr %0) +// LLVM: %[[ARG_STORAGE:.*]] = alloca ptr, i64 1 +// LLVM: %[[RETVAL:.*]] = alloca i8, i64 1 +// LLVM: %[[X_STORAGE:.*]] = alloca i8, i64 1 +// LLVM: store ptr %0, ptr %[[ARG_STORAGE]] +// LLVM: %[[LOADED_PTR:.*]] = load ptr, ptr %[[ARG_STORAGE]] +// LLVM: %[[NULL_CHECK:.*]] = icmp ne ptr %[[LOADED_PTR]], null +// LLVM: ret i1 void should_not_cast() { unsigned x1; diff --git a/clang/test/CIR/CodeGen/cmp.cpp b/clang/test/CIR/CodeGen/cmp.cpp index 57d6b5b411f98..a4f273a1d5802 100644 --- a/clang/test/CIR/CodeGen/cmp.cpp +++ b/clang/test/CIR/CodeGen/cmp.cpp @@ -1,7 +1,9 @@ -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -DCIR_ONLY %s -o %t.cir +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll // RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG void c0(int a, int b) { bool x = a > b; @@ -12,7 +14,7 @@ void c0(int a, int b) { x = a == b; } -// CIR: cir.func @c0( +// CIR-LABEL: cir.func @c0( // CIR: %[[A_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] // CIR: %[[B_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init] @@ -43,7 +45,7 @@ void c0(int a, int b) { // CIR: %[[B6:.*]] = cir.load %[[B_PTR]] // CIR: %{{.*}} = cir.cmp(eq, %[[A6]], %[[B6]]) : !s32i, !cir.bool -// LLVM: define void @c0(i32 %0, i32 %1) { +// LLVM-LABEL: define void @c0(i32 %0, i32 %1) { // LLVM: %[[PTR1:.*]] = alloca i32, i64 1 // LLVM: %[[PTR2:.*]] = alloca i32, i64 1 // LLVM: %[[BOOL_PTR:.*]] = alloca i8, i64 1 @@ -86,6 +88,49 @@ void c0(int a, int b) { // LLVM: %[[ZEXT6:.*]] = zext i1 %[[CMP6]] to i8 // LLVM: store i8 %[[ZEXT6]], ptr %[[BOOL_PTR]] +// OGCG-LABEL: define dso_local void @_Z2c0ii(i32 {{.*}} %a, i32 {{.*}} %b) {{.*}} { +// OGCG: %[[PTR1:.*]] = alloca i32 +// OGCG: %[[PTR2:.*]] = alloca i32 +// OGCG: %[[BOOL_PTR:.*]] = alloca i8 +// OGCG: store i32 %a, ptr %[[PTR1]] +// OGCG: store i32 %b, ptr %[[PTR2]] + +// OGCO: %[[A1:.*]] = load i32, ptr %[[PTR1]] +// OGCO: %[[B1:.*]] = load i32, ptr %[[PTR2]] +// OGCO: %[[CMP1:.*]] = icmp sgt i32 %[[A1]], %[[B1]] +// OGCO: %[[ZEXT1:.*]] = zext i1 %[[CMP1]] to i8 +// OGCO: store i8 %[[ZEXT1]], ptr %[[BOOL_PTR]] + +// OGCO: %[[A2:.*]] = load i32, ptr %[[PTR1]] +// OGCO: %[[B2:.*]] = load i32, ptr %[[PTR2]] +// OGCO: %[[CMP2:.*]] = icmp slt i32 %[[A2]], %[[B2]] +// OGCO: %[[ZEXT2:.*]] = zext i1 %[[CMP2]] to i8 +// OGCO: store i8 %[[ZEXT2]], ptr %[[BOOL_PTR]] + +// OGCO: %[[A3:.*]] = load i32, ptr %[[PTR1]] +// OGCO: %[[B3:.*]] = load i32, ptr %[[PTR2]] +// OGCO: %[[CMP3:.*]] = icmp sle i32 %[[A3]], %[[B3]] +// OGCO: %[[ZEXT3:.*]] = zext i1 %[[CMP3]] to i8 +// OGCO: store i8 %[[ZEXT3]], ptr %[[BOOL_PTR]] + +// OGCO: %[[A4:.*]] = load i32, ptr %[[PTR1]] +// OGCO: %[[B4:.*]] = load i32, ptr %[[PTR2]] +// OGCO: %[[CMP4:.*]] = icmp sge i32 %[[A4]], %[[B4]] +// OGCO: %[[ZEXT4:.*]] = zext i1 %[[CMP4]] to i8 +// OGCO: store i8 %[[ZEXT4]], ptr %[[BOOL_PTR]] + +// OGCO: %[[A5:.*]] = load i32, ptr %[[PTR1]] +// OGCO: %[[B5:.*]] = load i32, ptr %[[PTR2]] +// OGCO: %[[CMP5:.*]] = icmp ne i32 %[[A5]], %[[B5]] +// OGCO: %[[ZEXT5:.*]] = zext i1 %[[CMP5]] to i8 +// OGCO: store i8 %[[ZEXT5]], ptr %[[BOOL_PTR]] + +// OGCO: %[[A6:.*]] = load i32, ptr %[[PTR1]] +// OGCO: %[[B6:.*]] = load i32, ptr %[[PTR2]] +// OGCO: %[[CMP6:.*]] = icmp eq i32 %[[A6]], %[[B6]] +// OGCO: %[[ZEXT6:.*]] = zext i1 %[[CMP6]] to i8 +// OGCO: store i8 %[[ZEXT6]], ptr %[[BOOL_PTR]] + void c0_unsigned(unsigned int a, unsigned int b) { bool x = a > b; x = a < b; @@ -95,7 +140,7 @@ void c0_unsigned(unsigned int a, unsigned int b) { x = a == b; } -// CIR: cir.func @c0_unsigned( +// CIR-LABEL: cir.func @c0_unsigned( // CIR: %[[U_A_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["a", init] // CIR: %[[U_B_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["b", init] @@ -125,7 +170,7 @@ void c0_unsigned(unsigned int a, unsigned int b) { // CIR: %[[UB6:.*]] = cir.load %[[U_B_PTR]] // CIR: %{{.*}} = cir.cmp(eq, %[[UA6]], %[[UB6]]) : !u32i, !cir.bool -// LLVM: define void @c0_unsigned(i32 %0, i32 %1) { +// LLVM-LABEL: define void @c0_unsigned(i32 %0, i32 %1) { // LLVM: %[[U_PTR1:.*]] = alloca i32, i64 1 // LLVM: %[[U_PTR2:.*]] = alloca i32, i64 1 // LLVM: %[[U_BOOL_PTR:.*]] = alloca i8, i64 1 @@ -168,6 +213,49 @@ void c0_unsigned(unsigned int a, unsigned int b) { // LLVM: %[[UZEXT6:.*]] = zext i1 %[[UCMP6]] to i8 // LLVM: store i8 %[[UZEXT6]], ptr %[[U_BOOL_PTR]] +// OGCG-LABEL: define dso_local void @_Z11c0_unsignedjj(i32 {{.*}} %a, i32 {{.*}} %b) {{.*}} { +// OGCG: %[[U_PTR1:.*]] = alloca i32 +// OGCG: %[[U_PTR2:.*]] = alloca i32 +// OGCG: %[[U_BOOL_PTR:.*]] = alloca i8 +// OGCG: store i32 %a, ptr %[[U_PTR1]] +// OGCG: store i32 %b, ptr %[[U_PTR2]] + +// OGCG: %[[UA1:.*]] = load i32, ptr %[[U_PTR1]] +// OGCG: %[[UB1:.*]] = load i32, ptr %[[U_PTR2]] +// OGCG: %[[UCMP1:.*]] = icmp ugt i32 %[[UA1]], %[[UB1]] +// OGCG: %[[UZEXT1:.*]] = zext i1 %[[UCMP1]] to i8 +// OGCG: store i8 %[[UZEXT1]], ptr %[[U_BOOL_PTR]] + +// OGCG: %[[UA2:.*]] = load i32, ptr %[[U_PTR1]] +// OGCG: %[[UB2:.*]] = load i32, ptr %[[U_PTR2]] +// OGCG: %[[UCMP2:.*]] = icmp ult i32 %[[UA2]], %[[UB2]] +// OGCG: %[[UZEXT2:.*]] = zext i1 %[[UCMP2]] to i8 +// OGCG: store i8 %[[UZEXT2]], ptr %[[U_BOOL_PTR]] + +// OGCG: %[[UA3:.*]] = load i32, ptr %[[U_PTR1]] +// OGCG: %[[UB3:.*]] = load i32, ptr %[[U_PTR2]] +// OGCG: %[[UCMP3:.*]] = icmp ule i32 %[[UA3]], %[[UB3]] +// OGCG: %[[UZEXT3:.*]] = zext i1 %[[UCMP3]] to i8 +// OGCG: store i8 %[[UZEXT3]], ptr %[[U_BOOL_PTR]] + +// OGCG: %[[UA4:.*]] = load i32, ptr %[[U_PTR1]] +// OGCG: %[[UB4:.*]] = load i32, ptr %[[U_PTR2]] +// OGCG: %[[UCMP4:.*]] = icmp uge i32 %[[UA4]], %[[UB4]] +// OGCG: %[[UZEXT4:.*]] = zext i1 %[[UCMP4]] to i8 +// OGCG: store i8 %[[UZEXT4]], ptr %[[U_BOOL_PTR]] + +// OGCG: %[[UA5:.*]] = load i32, ptr %[[U_PTR1]] +// OGCG: %[[UB5:.*]] = load i32, ptr %[[U_PTR2]] +// OGCG: %[[UCMP5:.*]] = icmp ne i32 %[[UA5]], %[[UB5]] +// OGCG: %[[UZEXT5:.*]] = zext i1 %[[UCMP5]] to i8 +// OGCG: store i8 %[[UZEXT5]], ptr %[[U_BOOL_PTR]] + +// OGCG: %[[UA6:.*]] = load i32, ptr %[[U_PTR1]] +// OGCG: %[[UB6:.*]] = load i32, ptr %[[U_PTR2]] +// OGCG: %[[UCMP6:.*]] = icmp eq i32 %[[UA6]], %[[UB6]] +// OGCG: %[[UZEXT6:.*]] = zext i1 %[[UCMP6]] to i8 +// OGCG: store i8 %[[UZEXT6]], ptr %[[U_BOOL_PTR]] + void c0_float(float a, float b) { bool x = a > b; x = a < b; @@ -177,7 +265,7 @@ void c0_float(float a, float b) { x = a == b; } -// CIR: cir.func @c0_float(%arg0: !cir.float{{.*}}, %arg1: !cir.float{{.*}}) { +// CIR-LABEL: cir.func @c0_float(%arg0: !cir.float{{.*}}, %arg1: !cir.float{{.*}}) { // CIR: %[[A_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["a", init] // CIR: %[[B_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["b", init] // CIR: %[[X_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init] @@ -215,7 +303,7 @@ void c0_float(float a, float b) { // CIR: %[[CMP6:.*]] = cir.cmp(eq, %[[A6]], %[[B6]]) : !cir.float, !cir.bool // CIR: cir.store %[[CMP6]], %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> -// LLVM: define void @c0_float(float %0, float %1) { +// LLVM-LABEL: define void @c0_float(float %0, float %1) { // LLVM: %[[A_PTR:.*]] = alloca float // LLVM: %[[B_PTR:.*]] = alloca float // LLVM: store float %0, ptr %[[A_PTR]] @@ -231,3 +319,152 @@ void c0_float(float a, float b) { // LLVM: fcmp oge float %{{.*}}, %{{.*}} // LLVM: fcmp une float %{{.*}}, %{{.*}} // LLVM: fcmp oeq float %{{.*}}, %{{.*}} + +// OGCG-LABEL: define dso_local void @_Z8c0_floatff(float {{.*}} %a, float {{.*}} %b) {{.*}} { +// OGCG: %[[A_PTR:.*]] = alloca float +// OGCG: %[[B_PTR:.*]] = alloca float +// OGCG: store float %a, ptr %[[A_PTR]] +// OGCG: store float %b, ptr %[[B_PTR]] + +// OGCG: load float, ptr %[[A_PTR]] +// OGCG: load float, ptr %[[B_PTR]] +// OGCG: fcmp ogt float %{{.*}}, %{{.*}} +// OGCG: zext i1 %{{.*}} to i8 + +// OGCG: fcmp olt float %{{.*}}, %{{.*}} +// OGCG: fcmp ole float %{{.*}}, %{{.*}} +// OGCG: fcmp oge float %{{.*}}, %{{.*}} +// OGCG: fcmp une float %{{.*}}, %{{.*}} +// OGCG: fcmp oeq float %{{.*}}, %{{.*}} + +void pointer_cmp(int *a, int *b) { + bool x = a > b; + x = a < b; + x = a >= b; + x = a <= b; + x = a == b; + x = a != b; +} + +// CIR-LABEL: cir.func @pointer_cmp(%arg0: !cir.ptr<!s32i>{{.*}}, %arg1: !cir.ptr<!s32i>{{.*}}) { +// CIR: %[[A_PTR:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["b", init] + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i> +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i> +// CIR: %{{.*}} = cir.cmp(gt, %[[A1]], %[[B1]]) : !cir.ptr<!s32i>, !cir.bool + +// CIR: cir.cmp(lt, {{.*}}, {{.*}}) : !cir.ptr<!s32i>, !cir.bool +// CIR: cir.cmp(ge, {{.*}}, {{.*}}) : !cir.ptr<!s32i>, !cir.bool +// CIR: cir.cmp(le, {{.*}}, {{.*}}) : !cir.ptr<!s32i>, !cir.bool +// CIR: cir.cmp(eq, {{.*}}, {{.*}}) : !cir.ptr<!s32i>, !cir.bool +// CIR: cir.cmp(ne, {{.*}}, {{.*}}) : !cir.ptr<!s32i>, !cir.bool + +// LLVM-LABEL: define void @pointer_cmp(ptr %0, ptr %1) { +// LLVM: %[[A_PTR:.*]] = alloca ptr +// LLVM: %[[B_PTR:.*]] = alloca ptr +// LLVM: store ptr %0, ptr %[[A_PTR]] +// LLVM: store ptr %1, ptr %[[B_PTR]] + +// LLVM: load ptr, ptr %[[A_PTR]] +// LLVM: load ptr, ptr %[[B_PTR]] +// LLVM: icmp ugt ptr %{{.*}}, %{{.*}} +// LLVM: zext i1 %{{.*}} to i8 +// LLVM: icmp ult ptr %{{.*}}, %{{.*}} +// LLVM: icmp uge ptr %{{.*}}, %{{.*}} +// LLVM: icmp ule ptr %{{.*}}, %{{.*}} +// LLVM: icmp eq ptr %{{.*}}, %{{.*}} +// LLVM: icmp ne ptr %{{.*}}, %{{.*}} + +// OGCG-LABEL: define dso_local void @_Z11pointer_cmpPiS_(ptr {{.*}} %a, ptr {{.*}} %b) {{.*}} { +// OGCG: %[[A_PTR:.*]] = alloca ptr +// OGCG: %[[B_PTR:.*]] = alloca ptr +// OGCG: store ptr %a, ptr %[[A_PTR]] +// OGCG: store ptr %b, ptr %[[B_PTR]] + +// OGCG: load ptr, ptr %[[A_PTR]] +// OGCG: load ptr, ptr %[[B_PTR]] +// OGCG: icmp ugt ptr %{{.*}}, %{{.*}} +// OGCG: zext i1 %{{.*}} to i8 +// OGCG: icmp ult ptr %{{.*}}, %{{.*}} +// OGCG: icmp uge ptr %{{.*}}, %{{.*}} +// OGCG: icmp ule ptr %{{.*}}, %{{.*}} +// OGCG: icmp eq ptr %{{.*}}, %{{.*}} +// OGCG: icmp ne ptr %{{.*}}, %{{.*}} + +void bool_cmp(bool a, bool b) { + bool x = a > b; + x = a < b; + x = a >= b; + x = a <= b; + x = a == b; + x = a != b; +} + +// CIR-LABEL: cir.func @bool_cmp(%arg0: !cir.bool{{.*}}, %arg1: !cir.bool{{.*}}) { +// CIR: %[[A_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b", init] +// CIR: %[[X_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init] + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: %[[A1_INT:.*]] = cir.cast(bool_to_int, %[[A1]] : !cir.bool), !s32i +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: %[[B1_INT:.*]] = cir.cast(bool_to_int, %[[B1]] : !cir.bool), !s32i +// CIR: %{{.*}} = cir.cmp(gt, %[[A1_INT]], %[[B1_INT]]) : !s32i, !cir.bool +// CIR: cir.store {{.*}}, %[[X_PTR]] : !cir.bool, !cir.ptr<!cir.bool> + +// CIR: cir.cmp(lt +// CIR: cir.cmp(ge +// CIR: cir.cmp(le +// CIR: cir.cmp(eq +// CIR: cir.cmp(ne + +// LLVM-LABEL: define void @bool_cmp(i1 %0, i1 %1) { +// LLVM: %[[A_PTR:.*]] = alloca i8 +// LLVM: %[[B_PTR:.*]] = alloca i8 +// LLVM: %[[X_PTR:.*]] = alloca i8 +// LLVM: %[[A_INIT:.*]] = zext i1 %0 to i8 +// LLVM: store i8 %[[A_INIT]], ptr %[[A_PTR]] +// LLVM: %[[B_INIT:.*]] = zext i1 %1 to i8 +// LLVM: store i8 %[[B_INIT]], ptr %[[B_PTR]] + +// LLVM: %[[A1:.*]] = load i8, ptr %[[A_PTR]] +// LLVM: %[[A1_TRUNC:.*]] = trunc i8 %[[A1]] to i1 +// LLVM: %[[A1_EXT:.*]] = zext i1 %[[A1_TRUNC]] to i32 +// LLVM: %[[B1:.*]] = load i8, ptr %[[B_PTR]] +// LLVM: %[[B1_TRUNC:.*]] = trunc i8 %[[B1]] to i1 +// LLVM: %[[B1_EXT:.*]] = zext i1 %[[B1_TRUNC]] to i32 +// LLVM: %[[CMP1:.*]] = icmp sgt i32 %[[A1_EXT]], %[[B1_EXT]] +// LLVM: %[[CMP1_BOOL:.*]] = zext i1 %[[CMP1]] to i8 +// LLVM: store i8 %[[CMP1_BOOL]], ptr %[[X_PTR]] + +// LLVM: icmp slt +// LLVM: icmp sge +// LLVM: icmp sle +// LLVM: icmp eq +// LLVM: icmp ne + +// OGCG-LABEL: define dso_local void @_Z8bool_cmpbb(i1 {{.*}} %a, i1 {{.*}} %b) {{.*}} { +// OGCG: %[[A_PTR:.*]] = alloca i8 +// OGCG: %[[B_PTR:.*]] = alloca i8 +// OGCG: %[[X_PTR:.*]] = alloca i8 +// OGCG: %[[A_INIT:.*]] = zext i1 %a to i8 +// OGCG: store i8 %[[A_INIT]], ptr %[[A_PTR]] +// OGCG: %[[B_INIT:.*]] = zext i1 %b to i8 +// OGCG: store i8 %[[B_INIT]], ptr %[[B_PTR]] + +// OGCG: %[[A1:.*]] = load i8, ptr %[[A_PTR]] +// OGCG: %[[A1_TRUNC:.*]] = trunc i8 %[[A1]] to i1 +// OGCG: %[[A1_EXT:.*]] = zext i1 %[[A1_TRUNC]] to i32 +// OGCG: %[[B1:.*]] = load i8, ptr %[[B_PTR]] +// OGCG: %[[B1_TRUNC:.*]] = trunc i8 %[[B1]] to i1 +// OGCG: %[[B1_EXT:.*]] = zext i1 %[[B1_TRUNC]] to i32 +// OGCG: %[[CMP1:.*]] = icmp sgt i32 %[[A1_EXT]], %[[B1_EXT]] +// OGCG: %[[CMP1_BOOL:.*]] = zext i1 %[[CMP1]] to i8 +// OGCG: store i8 %[[CMP1_BOOL]], ptr %[[X_PTR]] + +// OGCG: icmp slt +// OGCG: icmp sge +// OGCG: icmp sle +// OGCG: icmp eq +// OGCG: icmp ne _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits