https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/133405
>From 13833779faad62f95ef3fc0e2de3ed9b7c44d2f5 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Fri, 11 Apr 2025 17:46:00 +0200 Subject: [PATCH 1/2] [CIR] Upstream SelectOp and ShiftOp Since SelectOp will only generated by a future pass that transforms a TernaryOp this only includes the lowering bits. This patchs also improves the testing of the existing binary operators. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 73 ++++ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 9 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 43 +++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 87 +++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 20 ++ clang/test/CIR/CodeGen/binop.cpp | 317 +++++++++++++++++- clang/test/CIR/Lowering/select.cir | 48 +++ 7 files changed, 579 insertions(+), 18 deletions(-) create mode 100644 clang/test/CIR/Lowering/select.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 0d3c2065cd58c..22195477e0137 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1176,6 +1176,79 @@ def BinOp : CIR_Op<"binop", [Pure, let hasVerifier = 1; } +//===----------------------------------------------------------------------===// +// ShiftOp +//===----------------------------------------------------------------------===// + +def ShiftOp : CIR_Op<"shift", [Pure]> { + let summary = "Shift"; + let description = [{ + Shift `left` or `right`, according to the first operand. Second operand is + the shift target and the third the amount. Second and the thrid operand are + integers. + + ```mlir + %7 = cir.shift(left, %1 : !u64i, %4 : !s32i) -> !u64i + ``` + }]; + + // TODO(cir): Support vectors. CIR_IntType -> CIR_AnyIntOrVecOfInt. Also + // update the description above. + let results = (outs CIR_IntType:$result); + let arguments = (ins CIR_IntType:$value, CIR_IntType:$amount, + UnitAttr:$isShiftleft); + + let assemblyFormat = [{ + `(` + (`left` $isShiftleft^) : (```right`)? + `,` $value `:` type($value) + `,` $amount `:` type($amount) + `)` `->` type($result) attr-dict + }]; + + let hasVerifier = 1; +} + +//===----------------------------------------------------------------------===// +// SelectOp +//===----------------------------------------------------------------------===// + +def SelectOp : CIR_Op<"select", [Pure, + AllTypesMatch<["true_value", "false_value", "result"]>]> { + let summary = "Yield one of two values based on a boolean value"; + let description = [{ + The `cir.select` operation takes three operands. The first operand + `condition` is a boolean value of type `!cir.bool`. The second and the third + operand can be of any CIR types, but their types must be the same. If the + first operand is `true`, the operation yields its second operand. Otherwise, + the operation yields its third operand. + + Example: + + ```mlir + %0 = cir.const #cir.bool<true> : !cir.bool + %1 = cir.const #cir.int<42> : !s32i + %2 = cir.const #cir.int<72> : !s32i + %3 = cir.select if %0 then %1 else %2 : (!cir.bool, !s32i, !s32i) -> !s32i + ``` + }]; + + let arguments = (ins CIR_BoolType:$condition, CIR_AnyType:$true_value, + CIR_AnyType:$false_value); + let results = (outs CIR_AnyType:$result); + + let assemblyFormat = [{ + `if` $condition `then` $true_value `else` $false_value + `:` `(` + qualified(type($condition)) `,` + qualified(type($true_value)) `,` + qualified(type($false_value)) + `)` `->` qualified(type($result)) attr-dict + }]; + + let hasFolder = 1; +} + //===----------------------------------------------------------------------===// // GlobalOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 38104f8533c7d..7417df4407925 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1308,8 +1308,9 @@ mlir::Value ScalarExprEmitter::emitShl(const BinOpInfo &ops) { mlir::isa<cir::IntType>(ops.lhs.getType())) cgf.cgm.errorNYI("sanitizers"); - cgf.cgm.errorNYI("shift ops"); - return {}; + return builder.create<cir::ShiftOp>(cgf.getLoc(ops.loc), + cgf.convertType(ops.fullType), ops.lhs, + ops.rhs, cgf.getBuilder().getUnitAttr()); } mlir::Value ScalarExprEmitter::emitShr(const BinOpInfo &ops) { @@ -1333,8 +1334,8 @@ mlir::Value ScalarExprEmitter::emitShr(const BinOpInfo &ops) { // Note that we don't need to distinguish unsigned treatment at this // point since it will be handled later by LLVM lowering. - cgf.cgm.errorNYI("shift ops"); - return {}; + return builder.create<cir::ShiftOp>( + cgf.getLoc(ops.loc), cgf.convertType(ops.fullType), ops.lhs, ops.rhs); } mlir::Value ScalarExprEmitter::emitAnd(const BinOpInfo &ops) { diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index f3e5e572653da..d25d6609a1aed 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -997,6 +997,9 @@ mlir::OpTrait::impl::verifySameFirstOperandAndResultType(Operation *op) { // been implemented yet. mlir::LogicalResult cir::FuncOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// BinOp +//===----------------------------------------------------------------------===// LogicalResult cir::BinOp::verify() { bool noWrap = getNoUnsignedWrap() || getNoSignedWrap(); bool saturated = getSaturated(); @@ -1028,6 +1031,46 @@ LogicalResult cir::BinOp::verify() { return mlir::success(); } +//===----------------------------------------------------------------------===// +// ShiftOp +//===----------------------------------------------------------------------===// +LogicalResult cir::ShiftOp::verify() { + mlir::Operation *op = getOperation(); + mlir::Type resType = getResult().getType(); + assert(!cir::MissingFeatures::vectorType()); + bool isOp0Vec = false; + bool isOp1Vec = false; + if (isOp0Vec != isOp1Vec) + return emitOpError() << "input types cannot be one vector and one scalar"; + if (isOp1Vec && op->getOperand(1).getType() != resType) { + return emitOpError() << "shift amount must have the type of the result " + << "if it is vector shift"; + } + return mlir::success(); +} + +//===----------------------------------------------------------------------===// +// SelectOp +//===----------------------------------------------------------------------===// + +OpFoldResult cir::SelectOp::fold(FoldAdaptor adaptor) { + mlir::Attribute condition = adaptor.getCondition(); + if (condition) { + bool conditionValue = mlir::cast<cir::BoolAttr>(condition).getValue(); + return conditionValue ? getTrueValue() : getFalseValue(); + } + + // cir.select if %0 then x else x -> x + mlir::Attribute trueValue = adaptor.getTrueValue(); + mlir::Attribute falseValue = adaptor.getFalseValue(); + if (trueValue && trueValue == falseValue) + return trueValue; + if (getTrueValue() == getFalseValue()) + return getTrueValue(); + + return {}; +} + //===----------------------------------------------------------------------===// // UnaryOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 7159f89c93a53..7f0c056182af9 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1294,6 +1294,91 @@ mlir::LogicalResult CIRToLLVMCmpOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMShiftOpLowering::matchAndRewrite( + cir::ShiftOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + auto cirAmtTy = mlir::dyn_cast<cir::IntType>(op.getAmount().getType()); + auto cirValTy = mlir::dyn_cast<cir::IntType>(op.getValue().getType()); + + // Operands could also be vector type + assert(!cir::MissingFeatures::vectorType()); + mlir::Type llvmTy = getTypeConverter()->convertType(op.getType()); + mlir::Value amt = adaptor.getAmount(); + mlir::Value val = adaptor.getValue(); + + // TODO(cir): Assert for vector types + assert((cirValTy && cirAmtTy) && + "shift input type must be integer or vector type, otherwise NYI"); + + assert((cirValTy == op.getType()) && "inconsistent operands' types NYI"); + + // Ensure shift amount is the same type as the value. Some undefined + // behavior might occur in the casts below as per [C99 6.5.7.3]. + // Vector type shift amount needs no cast as type consistency is expected to + // be already be enforced at CIRGen. + if (cirAmtTy) + amt = getLLVMIntCast(rewriter, amt, mlir::cast<mlir::IntegerType>(llvmTy), + !cirAmtTy.isSigned(), cirAmtTy.getWidth(), + cirValTy.getWidth()); + + // Lower to the proper LLVM shift operation. + if (op.getIsShiftleft()) { + rewriter.replaceOpWithNewOp<mlir::LLVM::ShlOp>(op, llvmTy, val, amt); + } else { + assert(!cir::MissingFeatures::vectorType()); + bool isUnsigned = !cirValTy.isSigned(); + if (isUnsigned) + rewriter.replaceOpWithNewOp<mlir::LLVM::LShrOp>(op, llvmTy, val, amt); + else + rewriter.replaceOpWithNewOp<mlir::LLVM::AShrOp>(op, llvmTy, val, amt); + } + + return mlir::success(); +} + +mlir::LogicalResult CIRToLLVMSelectOpLowering::matchAndRewrite( + cir::SelectOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + auto getConstantBool = [](mlir::Value value) -> std::optional<bool> { + auto definingOp = + mlir::dyn_cast_if_present<cir::ConstantOp>(value.getDefiningOp()); + if (!definingOp) + return std::nullopt; + + auto constValue = mlir::dyn_cast<cir::BoolAttr>(definingOp.getValue()); + if (!constValue) + return std::nullopt; + + return constValue.getValue(); + }; + + // Two special cases in the LLVMIR codegen of select op: + // - select %0, %1, false => and %0, %1 + // - select %0, true, %1 => or %0, %1 + if (mlir::isa<cir::BoolType>(op.getTrueValue().getType())) { + std::optional<bool> trueValue = getConstantBool(op.getTrueValue()); + std::optional<bool> falseValue = getConstantBool(op.getFalseValue()); + if (falseValue.has_value() && !*falseValue) { + // select %0, %1, false => and %0, %1 + rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(op, adaptor.getCondition(), + adaptor.getTrueValue()); + return mlir::success(); + } + if (trueValue.has_value() && *trueValue) { + // select %0, true, %1 => or %0, %1 + rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(op, adaptor.getCondition(), + adaptor.getFalseValue()); + return mlir::success(); + } + } + + mlir::Value llvmCondition = adaptor.getCondition(); + rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>( + op, llvmCondition, adaptor.getTrueValue(), adaptor.getFalseValue()); + + return mlir::success(); +} + static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, mlir::DataLayout &dataLayout) { converter.addConversion([&](cir::PointerType type) -> mlir::Type { @@ -1439,6 +1524,8 @@ void ConvertCIRToLLVMPass::runOnOperation() { CIRToLLVMConstantOpLowering, CIRToLLVMFuncOpLowering, CIRToLLVMGetGlobalOpLowering, + CIRToLLVMSelectOpLowering, + CIRToLLVMShiftOpLowering, CIRToLLVMTrapOpLowering, CIRToLLVMUnaryOpLowering // clang-format on diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index 1de6c9c56b485..acb872b200b55 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -209,6 +209,26 @@ class CIRToLLVMCmpOpLowering : public mlir::OpConversionPattern<cir::CmpOp> { mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMShiftOpLowering + : public mlir::OpConversionPattern<cir::ShiftOp> { +public: + using mlir::OpConversionPattern<cir::ShiftOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::ShiftOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + +class CIRToLLVMSelectOpLowering + : public mlir::OpConversionPattern<cir::SelectOp> { +public: + using mlir::OpConversionPattern<cir::SelectOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::SelectOp 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/binop.cpp b/clang/test/CIR/CodeGen/binop.cpp index 4c20f79600fac..22ee5d1cd1148 100644 --- a/clang/test/CIR/CodeGen/binop.cpp +++ b/clang/test/CIR/CodeGen/binop.cpp @@ -1,5 +1,9 @@ -// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -O1 -Wno-unused-value -fclangir -emit-cir %s -o %t.cir -// RUN: FileCheck --input-file=%t.cir %s +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c++11 -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 -std=c++11 -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 b0(int a, int b) { int x = a * b; @@ -12,22 +16,307 @@ void b0(int a, int b) { x = x | b; } -// CHECK: %{{.+}} = cir.binop(mul, %{{.+}}, %{{.+}}) nsw : !s32i -// CHECK: %{{.+}} = cir.binop(div, %{{.+}}, %{{.+}}) : !s32i -// CHECK: %{{.+}} = cir.binop(rem, %{{.+}}, %{{.+}}) : !s32i -// CHECK: %{{.+}} = cir.binop(add, %{{.+}}, %{{.+}}) nsw : !s32i -// CHECK: %{{.+}} = cir.binop(sub, %{{.+}}, %{{.+}}) nsw : !s32i -// CHECK: %{{.+}} = cir.binop(and, %{{.+}}, %{{.+}}) : !s32i -// CHECK: %{{.+}} = cir.binop(xor, %{{.+}}, %{{.+}}) : !s32i -// CHECK: %{{.+}} = cir.binop(or, %{{.+}}, %{{.+}}) : !s32i +// CIR-LABEL: cir.func @b0( +// CIR: %{{.+}} = cir.binop(mul, %{{.+}}, %{{.+}}) nsw : !s32i +// CIR: %{{.+}} = cir.binop(div, %{{.+}}, %{{.+}}) : !s32i +// CIR: %{{.+}} = cir.binop(rem, %{{.+}}, %{{.+}}) : !s32i +// CIR: %{{.+}} = cir.binop(add, %{{.+}}, %{{.+}}) nsw : !s32i +// CIR: %{{.+}} = cir.binop(sub, %{{.+}}, %{{.+}}) nsw : !s32i +// CIR: %{{.+}} = cir.binop(and, %{{.+}}, %{{.+}}) : !s32i +// CIR: %{{.+}} = cir.binop(xor, %{{.+}}, %{{.+}}) : !s32i +// CIR: %{{.+}} = cir.binop(or, %{{.+}}, %{{.+}}) : !s32i +// CIR: cir.return + +// LLVM-LABEL: define void @b0( +// LLVM-SAME: i32 %[[A:.*]], i32 %[[B:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i32 +// LLVM: %[[B_ADDR:.*]] = alloca i32 +// LLVM: %[[X:.*]] = alloca i32 +// LLVM: store i32 %[[A]], ptr %[[A_ADDR]] +// LLVM: store i32 %[[B]], ptr %[[B_ADDR]] + +// LLVM: %[[A:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[MUL:.*]] = mul nsw i32 %[[A]], %[[B]] +// LLVM: store i32 %[[MUL]], ptr %[[X]] + +// LLVM: %[[X1:.*]] = load i32, ptr %[[X]] +// LLVM: %[[B1:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[DIV:.*]] = sdiv i32 %[[X1]], %[[B1]] +// LLVM: store i32 %[[DIV]], ptr %[[X]] + +// LLVM: %[[X2:.*]] = load i32, ptr %[[X]] +// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[REM:.*]] = srem i32 %[[X2]], %[[B2]] +// LLVM: store i32 %[[REM]], ptr %[[X]] + +// LLVM: %[[X3:.*]] = load i32, ptr %[[X]] +// LLVM: %[[B3:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[ADD:.*]] = add nsw i32 %[[X3]], %[[B3]] +// LLVM: store i32 %[[ADD]], ptr %[[X]] + +// LLVM: %[[X4:.*]] = load i32, ptr %[[X]] +// LLVM: %[[B4:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[SUB:.*]] = sub nsw i32 %[[X4]], %[[B4]] +// LLVM: store i32 %[[SUB]], ptr %[[X]] + +// LLVM: %[[X5:.*]] = load i32, ptr %[[X]] +// LLVM: %[[B5:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[AND:.*]] = and i32 %[[X5]], %[[B5]] +// LLVM: store i32 %[[AND]], ptr %[[X]] + +// LLVM: %[[X6:.*]] = load i32, ptr %[[X]] +// LLVM: %[[B6:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[XOR:.*]] = xor i32 %[[X6]], %[[B6]] +// LLVM: store i32 %[[XOR]], ptr %[[X]] + +// LLVM: %[[X7:.*]] = load i32, ptr %[[X]] +// LLVM: %[[B7:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[OR:.*]] = or i32 %[[X7]], %[[B7]] +// LLVM: store i32 %[[OR]], ptr %[[X]] + +// LLVM: ret void + +// OGCG-LABEL: define dso_local void @_Z2b0ii(i32 {{.*}} %a, i32 {{.*}} %b) {{.*}} { +// OGCG: %[[A_ADDR:.*]] = alloca i32 +// OGCG: %[[B_ADDR:.*]] = alloca i32 +// OGCG: %[[X:.*]] = alloca i32 +// OGCG: store i32 %a, ptr %[[A_ADDR]] +// OGCG: store i32 %b, ptr %[[B_ADDR]] + +// OGCG: %[[A:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[MUL:.*]] = mul nsw i32 %[[A]], %[[B]] +// OGCG: store i32 %[[MUL]], ptr %[[X]] + +// OGCG: %[[X1:.*]] = load i32, ptr %[[X]] +// OGCG: %[[B1:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[DIV:.*]] = sdiv i32 %[[X1]], %[[B1]] +// OGCG: store i32 %[[DIV]], ptr %[[X]] + +// OGCG: %[[X2:.*]] = load i32, ptr %[[X]] +// OGCG: %[[B2:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[REM:.*]] = srem i32 %[[X2]], %[[B2]] +// OGCG: store i32 %[[REM]], ptr %[[X]] + +// OGCG: %[[X3:.*]] = load i32, ptr %[[X]] +// OGCG: %[[B3:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[ADD:.*]] = add nsw i32 %[[X3]], %[[B3]] +// OGCG: store i32 %[[ADD]], ptr %[[X]] + +// OGCG: %[[X4:.*]] = load i32, ptr %[[X]] +// OGCG: %[[B4:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[SUB:.*]] = sub nsw i32 %[[X4]], %[[B4]] +// OGCG: store i32 %[[SUB]], ptr %[[X]] + +// OGCG: %[[X5:.*]] = load i32, ptr %[[X]] +// OGCG: %[[B5:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[AND:.*]] = and i32 %[[X5]], %[[B5]] +// OGCG: store i32 %[[AND]], ptr %[[X]] + +// OGCG: %[[X6:.*]] = load i32, ptr %[[X]] +// OGCG: %[[B6:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[XOR:.*]] = xor i32 %[[X6]], %[[B6]] +// OGCG: store i32 %[[XOR]], ptr %[[X]] + +// OGCG: %[[X7:.*]] = load i32, ptr %[[X]] +// OGCG: %[[B7:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[OR:.*]] = or i32 %[[X7]], %[[B7]] +// OGCG: store i32 %[[OR]], ptr %[[X]] + +// OGCG: ret void void testFloatingPointBinOps(float a, float b) { a * b; - // CHECK: cir.binop(mul, %{{.+}}, %{{.+}}) : !cir.float a / b; - // CHECK: cir.binop(div, %{{.+}}, %{{.+}}) : !cir.float a + b; - // CHECK: cir.binop(add, %{{.+}}, %{{.+}}) : !cir.float a - b; - // CHECK: cir.binop(sub, %{{.+}}, %{{.+}}) : !cir.float } + +// CIR-LABEL: cir.func @testFloatingPointBinOps( +// CIR: cir.binop(mul, %{{.+}}, %{{.+}}) : !cir.float +// CIR: cir.binop(div, %{{.+}}, %{{.+}}) : !cir.float +// CIR: cir.binop(add, %{{.+}}, %{{.+}}) : !cir.float +// CIR: cir.binop(sub, %{{.+}}, %{{.+}}) : !cir.float +// CIR: cir.return + +// LLVM-LABEL: define void @testFloatingPointBinOps( +// LLVM-SAME: float %[[A:.*]], float %[[B:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca float, i64 1 +// LLVM: %[[B_ADDR:.*]] = alloca float, i64 1 +// LLVM: store float %[[A]], ptr %[[A_ADDR]] +// LLVM: store float %[[B]], ptr %[[B_ADDR]] + +// LLVM: %[[A1:.*]] = load float, ptr %[[A_ADDR]] +// LLVM: %[[B1:.*]] = load float, ptr %[[B_ADDR]] +// LLVM: fmul float %[[A1]], %[[B1]] + +// LLVM: %[[A2:.*]] = load float, ptr %[[A_ADDR]] +// LLVM: %[[B2:.*]] = load float, ptr %[[B_ADDR]] +// LLVM: fdiv float %[[A2]], %[[B2]] + +// LLVM: %[[A3:.*]] = load float, ptr %[[A_ADDR]] +// LLVM: %[[B3:.*]] = load float, ptr %[[B_ADDR]] +// LLVM: fadd float %[[A3]], %[[B3]] + +// LLVM: %[[A4:.*]] = load float, ptr %[[A_ADDR]] +// LLVM: %[[B4:.*]] = load float, ptr %[[B_ADDR]] +// LLVM: fsub float %[[A4]], %[[B4]] + +// LLVM: ret void + +// OGCG-LABEL: define dso_local void @_Z23testFloatingPointBinOpsff(float {{.*}} %a, float {{.*}} %b) +// OGCG: %a.addr = alloca float +// OGCG: %b.addr = alloca float +// OGCG: store float %a, ptr %a.addr +// OGCG: store float %b, ptr %b.addr + +// OGCG: %[[A1:.*]] = load float, ptr %a.addr +// OGCG: %[[B1:.*]] = load float, ptr %b.addr +// OGCG: fmul float %[[A1]], %[[B1]] + +// OGCG: %[[A2:.*]] = load float, ptr %a.addr +// OGCG: %[[B2:.*]] = load float, ptr %b.addr +// OGCG: fdiv float %[[A2]], %[[B2]] + +// OGCG: %[[A3:.*]] = load float, ptr %a.addr +// OGCG: %[[B3:.*]] = load float, ptr %b.addr +// OGCG: fadd float %[[A3]], %[[B3]] + +// OGCG: %[[A4:.*]] = load float, ptr %a.addr +// OGCG: %[[B4:.*]] = load float, ptr %b.addr +// OGCG: fsub float %[[A4]], %[[B4]] + +// OGCG: ret void + +void signed_shift(int a, int b) { + int x = a >> b; + x = a << b; +} + +// CIR-LABEL: cir.func @signed_shift( +// CIR-SAME: %[[ARG0:.*]]: !s32i{{.*}}, %[[ARG1:.*]]: !s32i{{.*}}) +// 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 !s32i, !cir.ptr<!s32i>, ["x", init] + +// CIR: cir.store %[[ARG0]], %[[A_PTR]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.store %[[ARG1]], %[[B_PTR]] : !s32i, !cir.ptr<!s32i> + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s32i, %[[B1]] : !s32i) -> !s32i +// CIR: cir.store %[[ASHR]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i> + +// CIR: %[[A2:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[B2:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s32i, %[[B2]] : !s32i) -> !s32i +// CIR: cir.store %[[SHL]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i> + +// CIR: cir.return + +// LLVM-LABEL: define void @signed_shift +// LLVM-SAME: (i32 %[[A:.*]], i32 %[[B:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i32 +// LLVM: %[[B_ADDR:.*]] = alloca i32 +// LLVM: %[[X:.*]] = alloca i32 +// LLVM: store i32 %[[A]], ptr %[[A_ADDR]] +// LLVM: store i32 %[[B]], ptr %[[B_ADDR]] + +// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B1:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1]] +// LLVM: store i32 %[[ASHR]], ptr %[[X]] + +// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]] +// LLVM: store i32 %[[SHL]], ptr %[[X]] + +// LLVM: ret void + +// OGCG-LABEL: define dso_local void @_Z12signed_shiftii +// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i32 {{.*}} %[[B:.*]]) +// OGCG: %[[A_ADDR:.*]] = alloca i32 +// OGCG: %[[B_ADDR:.*]] = alloca i32 +// OGCG: %[[X:.*]] = alloca i32 +// OGCG: store i32 %[[A]], ptr %[[A_ADDR]] +// OGCG: store i32 %[[B]], ptr %[[B_ADDR]] + +// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B1:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1]] +// OGCG: store i32 %[[ASHR]], ptr %[[X]] + +// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B2:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]] +// OGCG: store i32 %[[SHL]], ptr %[[X]] + +// OGCG: ret void + +void unsigned_shift(unsigned a, unsigned b) { + unsigned x = a >> b; + x = a << b; +} + +// CIR-LABEL: cir.func @unsigned_shift( +// CIR-SAME: %[[ARG0:.*]]: !u32i{{.*}}, %[[ARG1:.*]]: !u32i{{.*}}) +// CIR: %[[A_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["b", init] +// CIR: %[[X_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["x", init] + +// CIR: cir.store %[[ARG0]], %[[A_PTR]] : !u32i, !cir.ptr<!u32i> +// CIR: cir.store %[[ARG1]], %[[B_PTR]] : !u32i, !cir.ptr<!u32i> + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!u32i>, !u32i +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!u32i>, !u32i +// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !u32i, %[[B1]] : !u32i) -> !u32i +// CIR: cir.store %[[ASHR]], %[[X_PTR]] : !u32i, !cir.ptr<!u32i> + +// CIR: %[[A2:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!u32i>, !u32i +// CIR: %[[B2:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!u32i>, !u32i +// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !u32i, %[[B2]] : !u32i) -> !u32i +// CIR: cir.store %[[SHL]], %[[X_PTR]] : !u32i, !cir.ptr<!u32i> + +// CIR: cir.return + +// LLVM-LABEL: define void @unsigned_shift +// LLVM-SAME: (i32 %[[A:.*]], i32 %[[B:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i32 +// LLVM: %[[B_ADDR:.*]] = alloca i32 +// LLVM: %[[X:.*]] = alloca i32 +// LLVM: store i32 %[[A]], ptr %[[A_ADDR]] +// LLVM: store i32 %[[B]], ptr %[[B_ADDR]] + +// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B1:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[ASHR:.*]] = lshr i32 %[[A1]], %[[B1]] +// LLVM: store i32 %[[ASHR]], ptr %[[X]] + +// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]] +// LLVM: store i32 %[[SHL]], ptr %[[X]] + +// LLVM: ret void + +// OGCG-LABEL: define dso_local void @_Z14unsigned_shiftjj +// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i32 {{.*}} %[[B:.*]]) +// OGCG: %[[A_ADDR:.*]] = alloca i32 +// OGCG: %[[B_ADDR:.*]] = alloca i32 +// OGCG: %[[X:.*]] = alloca i32 +// OGCG: store i32 %[[A]], ptr %[[A_ADDR]] +// OGCG: store i32 %[[B]], ptr %[[B_ADDR]] + +// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B1:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[ASHR:.*]] = lshr i32 %[[A1]], %[[B1]] +// OGCG: store i32 %[[ASHR]], ptr %[[X]] + +// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B2:.*]] = load i32, ptr %[[B_ADDR]] +// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]] +// OGCG: store i32 %[[SHL]], ptr %[[X]] + +// OGCG: ret void diff --git a/clang/test/CIR/Lowering/select.cir b/clang/test/CIR/Lowering/select.cir new file mode 100644 index 0000000000000..71ca79a390e8b --- /dev/null +++ b/clang/test/CIR/Lowering/select.cir @@ -0,0 +1,48 @@ +// RUN: cir-translate -cir-to-llvmir --disable-cc-lowering -o %t.ll %s +// RUN: FileCheck --input-file=%t.ll -check-prefix=LLVM %s + +!s32i = !cir.int<s, 32> + +module { + cir.func @select_int(%arg0 : !cir.bool, %arg1 : !s32i, %arg2 : !s32i) -> !s32i { + %0 = cir.select if %arg0 then %arg1 else %arg2 : (!cir.bool, !s32i, !s32i) -> !s32i + cir.return %0 : !s32i + } + + // LLVM: define i32 @select_int(i1 %[[#COND:]], i32 %[[#TV:]], i32 %[[#FV:]]) + // LLVM-NEXT: %[[#RES:]] = select i1 %[[#COND]], i32 %[[#TV]], i32 %[[#FV]] + // LLVM-NEXT: ret i32 %[[#RES]] + // LLVM-NEXT: } + + cir.func @select_bool(%arg0 : !cir.bool, %arg1 : !cir.bool, %arg2 : !cir.bool) -> !cir.bool { + %0 = cir.select if %arg0 then %arg1 else %arg2 : (!cir.bool, !cir.bool, !cir.bool) -> !cir.bool + cir.return %0 : !cir.bool + } + + // LLVM: define i1 @select_bool(i1 %[[#COND:]], i1 %[[#TV:]], i1 %[[#FV:]]) + // LLVM-NEXT: %[[#RES:]] = select i1 %[[#COND]], i1 %[[#TV]], i1 %[[#FV]] + // LLVM-NEXT: ret i1 %[[#RES]] + // LLVM-NEXT: } + + cir.func @logical_and(%arg0 : !cir.bool, %arg1 : !cir.bool) -> !cir.bool { + %0 = cir.const #cir.bool<false> : !cir.bool + %1 = cir.select if %arg0 then %arg1 else %0 : (!cir.bool, !cir.bool, !cir.bool) -> !cir.bool + cir.return %1 : !cir.bool + } + + // LLVM: define i1 @logical_and(i1 %[[#ARG0:]], i1 %[[#ARG1:]]) + // LLVM-NEXT: %[[#RES:]] = and i1 %[[#ARG0]], %[[#ARG1]] + // LLVM-NEXT: ret i1 %[[#RES]] + // LLVM-NEXT: } + + cir.func @logical_or(%arg0 : !cir.bool, %arg1 : !cir.bool) -> !cir.bool { + %0 = cir.const #cir.bool<true> : !cir.bool + %1 = cir.select if %arg0 then %0 else %arg1 : (!cir.bool, !cir.bool, !cir.bool) -> !cir.bool + cir.return %1 : !cir.bool + } + + // LLVM: define i1 @logical_or(i1 %[[#ARG0:]], i1 %[[#ARG1:]]) + // LLVM-NEXT: %[[#RES:]] = or i1 %[[#ARG0]], %[[#ARG1]] + // LLVM-NEXT: ret i1 %[[#RES]] + // LLVM-NEXT: } +} >From 8199367b3221061c91d27bf91764be9cbc0ec188 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Mon, 14 Apr 2025 15:47:56 +0200 Subject: [PATCH 2/2] Address review feedback - add createShift functions to CIRBuilder - Rephrase ShiftOpn description comment - Remove folding for now - Always zero-extend instead of potentially sign-extend - Tests with diffferently sized integral types --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 38 +++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 10 +- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 7 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 22 -- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 21 +- clang/test/CIR/CodeGen/binop.cpp | 220 ++++++++++++++++++ 6 files changed, 276 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 68a4505ca7a5a..55c6f658ea43b 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -353,6 +353,44 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::CmpOp>(loc, getBoolTy(), kind, lhs, rhs); } + mlir::Value createShift(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, + bool isShiftLeft) { + return create<cir::ShiftOp>(loc, lhs.getType(), lhs, rhs, isShiftLeft); + } + + mlir::Value createShift(mlir::Location loc, mlir::Value lhs, + const llvm::APInt &rhs, bool isShiftLeft) { + return createShift(loc, lhs, getConstAPInt(loc, lhs.getType(), rhs), + isShiftLeft); + } + + mlir::Value createShift(mlir::Location loc, mlir::Value lhs, unsigned bits, + bool isShiftLeft) { + auto width = mlir::dyn_cast<cir::IntType>(lhs.getType()).getWidth(); + auto shift = llvm::APInt(width, bits); + return createShift(loc, lhs, shift, isShiftLeft); + } + + mlir::Value createShiftLeft(mlir::Location loc, mlir::Value lhs, + unsigned bits) { + return createShift(loc, lhs, bits, true); + } + + mlir::Value createShiftRight(mlir::Location loc, mlir::Value lhs, + unsigned bits) { + return createShift(loc, lhs, bits, false); + } + + mlir::Value createShiftLeft(mlir::Location loc, mlir::Value lhs, + mlir::Value rhs) { + return createShift(loc, lhs, rhs, true); + } + + mlir::Value createShiftRight(mlir::Location loc, mlir::Value lhs, + mlir::Value rhs) { + return createShift(loc, lhs, rhs, false); + } + // // Block handling helpers // ---------------------- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 22195477e0137..6b40ba20cd6a3 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1183,9 +1183,11 @@ def BinOp : CIR_Op<"binop", [Pure, def ShiftOp : CIR_Op<"shift", [Pure]> { let summary = "Shift"; let description = [{ - Shift `left` or `right`, according to the first operand. Second operand is - the shift target and the third the amount. Second and the thrid operand are - integers. + The `cir.shift` operation performs a bitwise shift, either to the left or to + the right, based on the first operand. The second operand specifies the + value to be shifted, and the third operand determines the number of + positions by which the shift is applied. Both the second and third operands + are required to be integers. ```mlir %7 = cir.shift(left, %1 : !u64i, %4 : !s32i) -> !u64i @@ -1245,8 +1247,6 @@ def SelectOp : CIR_Op<"select", [Pure, qualified(type($false_value)) `)` `->` qualified(type($result)) attr-dict }]; - - let hasFolder = 1; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 7417df4407925..2deb66b226145 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1308,9 +1308,7 @@ mlir::Value ScalarExprEmitter::emitShl(const BinOpInfo &ops) { mlir::isa<cir::IntType>(ops.lhs.getType())) cgf.cgm.errorNYI("sanitizers"); - return builder.create<cir::ShiftOp>(cgf.getLoc(ops.loc), - cgf.convertType(ops.fullType), ops.lhs, - ops.rhs, cgf.getBuilder().getUnitAttr()); + return builder.createShiftLeft(cgf.getLoc(ops.loc), ops.lhs, ops.rhs); } mlir::Value ScalarExprEmitter::emitShr(const BinOpInfo &ops) { @@ -1334,8 +1332,7 @@ mlir::Value ScalarExprEmitter::emitShr(const BinOpInfo &ops) { // Note that we don't need to distinguish unsigned treatment at this // point since it will be handled later by LLVM lowering. - return builder.create<cir::ShiftOp>( - cgf.getLoc(ops.loc), cgf.convertType(ops.fullType), ops.lhs, ops.rhs); + return builder.createShiftRight(cgf.getLoc(ops.loc), ops.lhs, ops.rhs); } mlir::Value ScalarExprEmitter::emitAnd(const BinOpInfo &ops) { diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index d25d6609a1aed..bb85a8aead800 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1049,28 +1049,6 @@ LogicalResult cir::ShiftOp::verify() { return mlir::success(); } -//===----------------------------------------------------------------------===// -// SelectOp -//===----------------------------------------------------------------------===// - -OpFoldResult cir::SelectOp::fold(FoldAdaptor adaptor) { - mlir::Attribute condition = adaptor.getCondition(); - if (condition) { - bool conditionValue = mlir::cast<cir::BoolAttr>(condition).getValue(); - return conditionValue ? getTrueValue() : getFalseValue(); - } - - // cir.select if %0 then x else x -> x - mlir::Attribute trueValue = adaptor.getTrueValue(); - mlir::Attribute falseValue = adaptor.getFalseValue(); - if (trueValue && trueValue == falseValue) - return trueValue; - if (getTrueValue() == getFalseValue()) - return getTrueValue(); - - return {}; -} - //===----------------------------------------------------------------------===// // UnaryOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 7f0c056182af9..ec43b666830b8 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -19,6 +19,7 @@ #include "mlir/Dialect/DLTI/DLTI.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/BuiltinDialect.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Types.h" @@ -28,6 +29,7 @@ #include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" #include "mlir/Target/LLVMIR/Export.h" #include "mlir/Transforms/DialectConversion.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/Passes.h" #include "clang/CIR/LoweringHelpers.h" @@ -1318,8 +1320,7 @@ mlir::LogicalResult CIRToLLVMShiftOpLowering::matchAndRewrite( // be already be enforced at CIRGen. if (cirAmtTy) amt = getLLVMIntCast(rewriter, amt, mlir::cast<mlir::IntegerType>(llvmTy), - !cirAmtTy.isSigned(), cirAmtTy.getWidth(), - cirValTy.getWidth()); + true, cirAmtTy.getWidth(), cirValTy.getWidth()); // Lower to the proper LLVM shift operation. if (op.getIsShiftleft()) { @@ -1339,32 +1340,32 @@ mlir::LogicalResult CIRToLLVMShiftOpLowering::matchAndRewrite( mlir::LogicalResult CIRToLLVMSelectOpLowering::matchAndRewrite( cir::SelectOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { - auto getConstantBool = [](mlir::Value value) -> std::optional<bool> { + auto getConstantBool = [](mlir::Value value) -> cir::BoolAttr { auto definingOp = mlir::dyn_cast_if_present<cir::ConstantOp>(value.getDefiningOp()); if (!definingOp) - return std::nullopt; + return {}; auto constValue = mlir::dyn_cast<cir::BoolAttr>(definingOp.getValue()); if (!constValue) - return std::nullopt; + return {}; - return constValue.getValue(); + return constValue; }; // Two special cases in the LLVMIR codegen of select op: // - select %0, %1, false => and %0, %1 // - select %0, true, %1 => or %0, %1 if (mlir::isa<cir::BoolType>(op.getTrueValue().getType())) { - std::optional<bool> trueValue = getConstantBool(op.getTrueValue()); - std::optional<bool> falseValue = getConstantBool(op.getFalseValue()); - if (falseValue.has_value() && !*falseValue) { + cir::BoolAttr trueValue = getConstantBool(op.getTrueValue()); + cir::BoolAttr falseValue = getConstantBool(op.getFalseValue()); + if (falseValue && !falseValue.getValue()) { // select %0, %1, false => and %0, %1 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(op, adaptor.getCondition(), adaptor.getTrueValue()); return mlir::success(); } - if (trueValue.has_value() && *trueValue) { + if (trueValue && trueValue.getValue()) { // select %0, true, %1 => or %0, %1 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(op, adaptor.getCondition(), adaptor.getFalseValue()); diff --git a/clang/test/CIR/CodeGen/binop.cpp b/clang/test/CIR/CodeGen/binop.cpp index 22ee5d1cd1148..92816cd6b1f8a 100644 --- a/clang/test/CIR/CodeGen/binop.cpp +++ b/clang/test/CIR/CodeGen/binop.cpp @@ -320,3 +320,223 @@ void unsigned_shift(unsigned a, unsigned b) { // OGCG: store i32 %[[SHL]], ptr %[[X]] // OGCG: ret void + +void zext_shift_example(int a, unsigned char b) { + int x = a >> b; + x = a << b; +} + +// CIR-LABEL: cir.func @zext_shift_example( +// CIR-SAME: %[[ARG0:.*]]: !s32i{{.*}}, %[[ARG1:.*]]: !u8i{{.*}}) +// CIR: %[[A_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !u8i, !cir.ptr<!u8i>, ["b", init] +// CIR: %[[X_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] + +// CIR: cir.store %[[ARG0]], %[[A_PTR]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.store %[[ARG1]], %[[B_PTR]] : !u8i, !cir.ptr<!u8i> + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!u8i>, !u8i +// CIR: %[[B1_EXT:.*]] = cir.cast(integral, %[[B1]] : !u8i), !s32i +// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s32i, %[[B1_EXT]] : !s32i) -> !s32i +// CIR: cir.store %[[ASHR]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i> + +// CIR: %[[A2:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[B2:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!u8i>, !u8i +// CIR: %[[B2_EXT:.*]] = cir.cast(integral, %[[B2]] : !u8i), !s32i +// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s32i, %[[B2_EXT]] : !s32i) -> !s32i +// CIR: cir.store %[[SHL]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i> + +// CIR: cir.return + +// LLVM-LABEL: define void @zext_shift_example +// LLVM-SAME: (i32 %[[A:.*]], i8 %[[B:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i32 +// LLVM: %[[B_ADDR:.*]] = alloca i8 +// LLVM: %[[X:.*]] = alloca i32 +// LLVM: store i32 %[[A]], ptr %[[A_ADDR]] +// LLVM: store i8 %[[B]], ptr %[[B_ADDR]] + +// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B1:.*]] = load i8, ptr %[[B_ADDR]] +// LLVM: %[[B1_EXT:.*]] = zext i8 %[[B1]] to i32 +// LLVM: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]] +// LLVM: store i32 %[[ASHR]], ptr %[[X]] + +// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B2:.*]] = load i8, ptr %[[B_ADDR]] +// LLVM: %[[B2_EXT:.*]] = zext i8 %[[B2]] to i32 +// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]] +// LLVM: store i32 %[[SHL]], ptr %[[X]] + +// LLVM: ret void + +// OGCG-LABEL: define dso_local void @_Z18zext_shift_exampleih +// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i8 {{.*}} %[[B:.*]]) +// OGCG: %[[A_ADDR:.*]] = alloca i32 +// OGCG: %[[B_ADDR:.*]] = alloca i8 +// OGCG: %[[X:.*]] = alloca i32 +// OGCG: store i32 %[[A]], ptr %[[A_ADDR]] +// OGCG: store i8 %[[B]], ptr %[[B_ADDR]] + +// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B1:.*]] = load i8, ptr %[[B_ADDR]] +// OGCG: %[[B1_EXT:.*]] = zext i8 %[[B1]] to i32 +// OGCG: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]] +// OGCG: store i32 %[[ASHR]], ptr %[[X]] + +// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B2:.*]] = load i8, ptr %[[B_ADDR]] +// OGCG: %[[B2_EXT:.*]] = zext i8 %[[B2]] to i32 +// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]] +// OGCG: store i32 %[[SHL]], ptr %[[X]] + +// OGCG: ret void + +void sext_shift_example(int a, signed char b) { + int x = a >> b; + x = a << b; +} + +// CIR-LABEL: cir.func @sext_shift_example( +// CIR-SAME: %[[ARG0:.*]]: !s32i{{.*}}, %[[ARG1:.*]]: !s8i{{.*}}) +// CIR: %[[A_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !s8i, !cir.ptr<!s8i>, ["b", init] +// CIR: %[[X_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] + +// CIR: cir.store %[[ARG0]], %[[A_PTR]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.store %[[ARG1]], %[[B_PTR]] : !s8i, !cir.ptr<!s8i> + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!s8i>, !s8i +// CIR: %[[B1_EXT:.*]] = cir.cast(integral, %[[B1]] : !s8i), !s32i +// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s32i, %[[B1_EXT]] : !s32i) -> !s32i +// CIR: cir.store %[[ASHR]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i> + +// CIR: %[[A2:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[B2:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!s8i>, !s8i +// CIR: %[[B2_EXT:.*]] = cir.cast(integral, %[[B2]] : !s8i), !s32i +// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s32i, %[[B2_EXT]] : !s32i) -> !s32i +// CIR: cir.store %[[SHL]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i> + +// CIR: cir.return + +// LLVM-LABEL: define void @sext_shift_example +// LLVM-SAME: (i32 %[[A:.*]], i8 %[[B:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i32 +// LLVM: %[[B_ADDR:.*]] = alloca i8 +// LLVM: %[[X:.*]] = alloca i32 +// LLVM: store i32 %[[A]], ptr %[[A_ADDR]] +// LLVM: store i8 %[[B]], ptr %[[B_ADDR]] + +// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B1:.*]] = load i8, ptr %[[B_ADDR]] +// LLVM: %[[B1_EXT:.*]] = sext i8 %[[B1]] to i32 +// LLVM: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]] +// LLVM: store i32 %[[ASHR]], ptr %[[X]] + +// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B2:.*]] = load i8, ptr %[[B_ADDR]] +// LLVM: %[[B2_EXT:.*]] = sext i8 %[[B2]] to i32 +// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]] +// LLVM: store i32 %[[SHL]], ptr %[[X]] + +// LLVM: ret void + +// OGCG-LABEL: define dso_local void @_Z18sext_shift_exampleia +// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i8 {{.*}} %[[B:.*]]) +// OGCG: %[[A_ADDR:.*]] = alloca i32 +// OGCG: %[[B_ADDR:.*]] = alloca i8 +// OGCG: %[[X:.*]] = alloca i32 +// OGCG: store i32 %[[A]], ptr %[[A_ADDR]] +// OGCG: store i8 %[[B]], ptr %[[B_ADDR]] + +// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B1:.*]] = load i8, ptr %[[B_ADDR]] +// OGCG: %[[B1_EXT:.*]] = sext i8 %[[B1]] to i32 +// OGCG: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]] +// OGCG: store i32 %[[ASHR]], ptr %[[X]] + +// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]] +// OGCG: %[[B2:.*]] = load i8, ptr %[[B_ADDR]] +// OGCG: %[[B2_EXT:.*]] = sext i8 %[[B2]] to i32 +// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]] +// OGCG: store i32 %[[SHL]], ptr %[[X]] + +// OGCG: ret void + +void long_shift_example(long long a, short b) { + long long x = a >> b; + x = a << b; +} + +// CIR-LABEL: cir.func @long_shift_example( +// CIR-SAME: %[[ARG0:.*]]: !s64i{{.*}}, %[[ARG1:.*]]: !s16i{{.*}}) +// CIR: %[[A_PTR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["a", init] +// CIR: %[[B_PTR:.*]] = cir.alloca !s16i, !cir.ptr<!s16i>, ["b", init] +// CIR: %[[X_PTR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["x", init] + +// CIR: cir.store %[[ARG0]], %[[A_PTR]] : !s64i, !cir.ptr<!s64i> +// CIR: cir.store %[[ARG1]], %[[B_PTR]] : !s16i, !cir.ptr<!s16i> + +// CIR: %[[A1:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s64i>, !s64i +// CIR: %[[B1:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!s16i>, !s16i +// CIR: %[[B1_EXT:.*]] = cir.cast(integral, %[[B1]] : !s16i), !s32i +// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s64i, %[[B1_EXT]] : !s32i) -> !s64i +// CIR: cir.store %[[ASHR]], %[[X_PTR]] : !s64i, !cir.ptr<!s64i> + +// CIR: %[[A2:.*]] = cir.load %[[A_PTR]] : !cir.ptr<!s64i>, !s64i +// CIR: %[[B2:.*]] = cir.load %[[B_PTR]] : !cir.ptr<!s16i>, !s16i +// CIR: %[[B2_EXT:.*]] = cir.cast(integral, %[[B2]] : !s16i), !s32i +// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s64i, %[[B2_EXT]] : !s32i) -> !s64i +// CIR: cir.store %[[SHL]], %[[X_PTR]] : !s64i, !cir.ptr<!s64i> + +// CIR: cir.return + +// LLVM-LABEL: define void @long_shift_example +// LLVM-SAME: (i64 %[[A:.*]], i16 %[[B:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i64 +// LLVM: %[[B_ADDR:.*]] = alloca i16 +// LLVM: %[[X:.*]] = alloca i64 +// LLVM: store i64 %[[A]], ptr %[[A_ADDR]] +// LLVM: store i16 %[[B]], ptr %[[B_ADDR]] + +// LLVM: %[[A1:.*]] = load i64, ptr %[[A_ADDR]] +// LLVM: %[[B1:.*]] = load i16, ptr %[[B_ADDR]] +// LLVM: %[[B1_SEXT:.*]] = sext i16 %[[B1]] to i32 +// LLVM: %[[B1_ZEXT:.*]] = zext i32 %[[B1_SEXT]] to i64 +// LLVM: %[[ASHR:.*]] = ashr i64 %[[A1]], %[[B1_ZEXT]] +// LLVM: store i64 %[[ASHR]], ptr %[[X]] + +// LLVM: %[[A2:.*]] = load i64, ptr %[[A_ADDR]] +// LLVM: %[[B2:.*]] = load i16, ptr %[[B_ADDR]] +// LLVM: %[[B2_SEXT:.*]] = sext i16 %[[B2]] to i32 +// LLVM: %[[B2_ZEXT:.*]] = zext i32 %[[B2_SEXT]] to i64 +// LLVM: %[[SHL:.*]] = shl i64 %[[A2]], %[[B2_ZEXT]] +// LLVM: store i64 %[[SHL]], ptr %[[X]] + +// LLVM: ret void + +// OGCG-LABEL: define dso_local void @_Z18long_shift_examplexs +// OGCG-SAME: (i64 {{.*}} %[[A:.*]], i16 {{.*}} %[[B:.*]]) +// OGCG: %[[A_ADDR:.*]] = alloca i64 +// OGCG: %[[B_ADDR:.*]] = alloca i16 +// OGCG: %[[X:.*]] = alloca i64 +// OGCG: store i64 %[[A]], ptr %[[A_ADDR]] +// OGCG: store i16 %[[B]], ptr %[[B_ADDR]] + +// OGCG: %[[A1:.*]] = load i64, ptr %[[A_ADDR]] +// OGCG: %[[B1:.*]] = load i16, ptr %[[B_ADDR]] +// OGCG: %[[B1_SEXT:.*]] = sext i16 %[[B1]] to i32 +// OGCG: %[[B1_ZEXT:.*]] = zext i32 %[[B1_SEXT]] to i64 +// OGCG: %[[ASHR:.*]] = ashr i64 %[[A1]], %[[B1_ZEXT]] +// OGCG: store i64 %[[ASHR]], ptr %[[X]] + +// OGCG: %[[A2:.*]] = load i64, ptr %[[A_ADDR]] +// OGCG: %[[B2:.*]] = load i16, ptr %[[B_ADDR]] +// OGCG: %[[B2_SEXT:.*]] = sext i16 %[[B2]] to i32 +// OGCG: %[[B2_ZEXT:.*]] = zext i32 %[[B2_SEXT]] to i64 +// OGCG: %[[SHL:.*]] = shl i64 %[[A2]], %[[B2_ZEXT]] +// OGCG: store i64 %[[SHL]], ptr %[[X]] + +// OGCG: ret void _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits