From 88a68dbc50a6a91933c2974d369e404c29facd48 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Sat, 17 Sep 2022 22:18:43 -0700 Subject: [PATCH 1/3] [CIR] Add cir::UnaryOp operation For now covering increment/decrement. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 47 ++++++++++++++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 27 +++++++++++ clang/test/CIR/IR/invalid.cir | 23 ++++++++++ 3 files changed, 97 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 51bed87614ff..b15011573ae2 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -547,6 +547,53 @@ def ScopeOp : CIR_Op<"scope", [DeclareOpInterfaceMethods; +def UnaryOpKind_Dec : I32EnumAttrCase<"Dec", 2, "dec">; + +def UnaryOpKind : I32EnumAttr< + "UnaryOpKind", + "unary operation kind", + [UnaryOpKind_Inc, + UnaryOpKind_Dec]> { + let cppNamespace = "::mlir::cir"; +} + +// FIXME: NoSideEffect won't work when we add overloading. +def UnaryOp : CIR_Op<"unary", + [NoSideEffect, + SameOperandsAndResultType]> { + let summary = "Unary operations"; + let description = [{ + `cir.unary` performs the unary operation according to + the specified opcode kind: [inc, dec]. + + Note for inc and dec: the operation corresponds only to the + addition/subtraction, its input is expect to come from a load + and the result to be used by a corresponding store. + + It requires one input operand and has one result, both types + should be the same. + + ```mlir + %7 = cir.unary(inc, %1) : i32 -> i32 + %8 = cir.unary(dec, %2) : i32 -> i32 + ``` + }]; + + let results = (outs AnyType:$result); + let arguments = (ins Arg:$kind, Arg:$input); + + let assemblyFormat = [{ + `(` $kind `,` $input `)` `:` type($input) `,` type($result) attr-dict + }]; + + let hasVerifier = 1; +} + //===----------------------------------------------------------------------===// // BinOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index a6a354c4eb83..3f1f664d4cb1 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1274,6 +1274,33 @@ FunctionType CallOp::getCalleeType() { return FunctionType::get(getContext(), getOperandTypes(), getResultTypes()); } +//===----------------------------------------------------------------------===// +// UnaryOp +//===----------------------------------------------------------------------===// + +LogicalResult UnaryOp::verify() { + switch (getKind()) { + case cir::UnaryOpKind::Inc: + LLVM_FALLTHROUGH; + case cir::UnaryOpKind::Dec: { + // TODO: Consider looking at the memory interface instead of LoadOp/StoreOp. + auto loadOp = getInput().getDefiningOp(); + if (!loadOp) + return emitOpError() << "requires input to be defined by a memory load"; + + for (const auto user : getResult().getUsers()) { + auto storeOp = dyn_cast(user); + if (storeOp && storeOp.getAddr() == loadOp.getAddr()) + return success(); + } + return emitOpError() << "requires result to be used by a memory store " + "to the same address as the input memory load"; + } + } + + llvm_unreachable("Unknown UnaryOp kind?"); +} + //===----------------------------------------------------------------------===// // CIR defined traits //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/IR/invalid.cir b/clang/test/CIR/IR/invalid.cir index 998da7fa3fb8..6bd947f4ca9e 100644 --- a/clang/test/CIR/IR/invalid.cir +++ b/clang/test/CIR/IR/invalid.cir @@ -198,3 +198,26 @@ module { module { cir.global "public" internal @v = 3 : i32 // expected-error {{public visibility not allowed with 'internal' linkage}} } + +// ----- + +cir.func @unary0() { + %0 = cir.alloca i32, cir.ptr , ["a", cinit] {alignment = 4 : i64} + %1 = cir.cst(2 : i32) : i32 + + %3 = cir.unary(inc, %1) : i32, i32 // expected-error {{'cir.unary' op requires input to be defined by a memory load}} + cir.store %3, %0 : i32, cir.ptr + cir.return +} + +// ----- + +cir.func @unary1() { + %0 = cir.alloca i32, cir.ptr , ["a", cinit] {alignment = 4 : i64} + %1 = cir.cst(2 : i32) : i32 + cir.store %1, %0 : i32, cir.ptr + + %2 = cir.load %0 : cir.ptr , i32 + %3 = cir.unary(dec, %2) : i32, i32 // // expected-error {{'cir.unary' op requires result to be used by a memory store to the same address as the input memory load}} + cir.return +} From e67839a5d821deccab2aa9ee0194ce9ce46a0217 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Sun, 4 Sep 2022 00:59:18 -0700 Subject: [PATCH 2/3] [CIR][CodeGen] Add basic support for increment/decrement --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 112 ++++++++++++++++++++- clang/test/CIR/CodeGen/inc-dec.cpp | 55 ++++++++++ 2 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 clang/test/CIR/CodeGen/inc-dec.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index b9a73bb1748d..11affb9809a2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -216,16 +216,113 @@ class ScalarExprEmitter : public StmtVisitor { // Unary Operators. mlir::Value VisitUnaryPostDec(const UnaryOperator *E) { - llvm_unreachable("NYI"); + return buildScalarPrePostIncDec(E); } mlir::Value VisitUnaryPostInc(const UnaryOperator *E) { - llvm_unreachable("NYI"); + return buildScalarPrePostIncDec(E); } mlir::Value VisitUnaryPreDec(const UnaryOperator *E) { - llvm_unreachable("NYI"); + return buildScalarPrePostIncDec(E); } mlir::Value VisitUnaryPreInc(const UnaryOperator *E) { - llvm_unreachable("NYI"); + return buildScalarPrePostIncDec(E); + } + mlir::Value buildScalarPrePostIncDec(const UnaryOperator *E) { + QualType type = E->getSubExpr()->getType(); + + auto LV = CGF.buildLValue(E->getSubExpr()); + mlir::Value Value; + mlir::Value Input; + + if (const AtomicType *atomicTy = type->getAs()) { + assert(0 && "no atomics inc/dec yet"); + } else { + Value = buildLoadOfLValue(LV, E->getExprLoc()); + Input = Value; + } + + // NOTE: When possible, more frequent cases are handled first. + + // Special case of integer increment that we have to check first: bool++. + // Due to promotion rules, we get: + // bool++ -> bool = bool + 1 + // -> bool = (int)bool + 1 + // -> bool = ((int)bool + 1 != 0) + // An interesting aspect of this is that increment is always true. + // Decrement does not have this property. + if (E->isIncrementOp() && type->isBooleanType()) { + assert(0 && "inc simplification for booleans not implemented yet"); + + // NOTE: We likely want the code below, but loading/store booleans need to work first. + // See CIRGenFunction::buildFromMemory(). + Value = Builder.create(CGF.getLoc(E->getExprLoc()), + CGF.getCIRType(type), + Builder.getBoolAttr(true)); + } else if (type->isIntegerType()) { + bool canPerformLossyDemotionCheck = false; + if (type->isPromotableIntegerType()) { + canPerformLossyDemotionCheck = true; + assert(0 && "no promotable integer inc/dec yet"); + } + + if (CGF.SanOpts.hasOneOf( + SanitizerKind::ImplicitIntegerArithmeticValueChange) && + canPerformLossyDemotionCheck) { + assert(0 && + "perform lossy demotion case for inc/dec not implemented yet"); + } else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) { + Value = buildIncDecConsiderOverflowBehavior(E, Value); + } else if (E->canOverflow() && type->isUnsignedIntegerType() && + CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) { + assert(0 && + "unsigned integer overflow sanitized inc/dec not implemented"); + } else { + auto Kind = E->isIncrementOp() ? mlir::cir::UnaryOpKind::Inc + : mlir::cir::UnaryOpKind::Dec; + Value = buildUnaryOp(E, Kind, Input); + } + } else if (const PointerType *ptr = type->getAs()) { + assert(0 && "no pointer inc/dec yet"); + } else if (type->isVectorType()) { + assert(0 && "no vector inc/dec yet"); + } else if (type->isRealFloatingType()) { + assert(0 && "no float inc/dec yet"); + } else if (type->isFixedPointType()) { + assert(0 && "no fixed point inc/dec yet"); + } else { + assert(type->castAs()); + assert(0 && "no objc pointer type inc/dec yet"); + } + + CIRGenFunction::SourceLocRAIIObject sourceloc{ + CGF, CGF.getLoc(E->getSourceRange())}; + + if (LV.isBitField()) + assert(0 && "no bitfield inc/dec yet"); + else + CGF.buildStoreThroughLValue(RValue::get(Value), LV, nullptr); + + return E->isPrefix() ? Value : Input; + } + + mlir::Value buildIncDecConsiderOverflowBehavior(const UnaryOperator *E, + mlir::Value V) { + switch (CGF.getLangOpts().getSignedOverflowBehavior()) { + case LangOptions::SOB_Defined: { + auto Kind = E->isIncrementOp() ? mlir::cir::UnaryOpKind::Inc + : mlir::cir::UnaryOpKind::Dec; + return buildUnaryOp(E, Kind, V); + break; + } + case LangOptions::SOB_Undefined: + assert(0 && + "inc/dec overflow behavior SOB_Undefined not implemented yet"); + break; + case LangOptions::SOB_Trapping: + assert(0 && "inc/dec overflow behavior SOB_Trapping not implemented yet"); + break; + } + llvm_unreachable("Unknown SignedOverflowBehaviorTy"); } mlir::Value VisitUnaryAddrOf(const UnaryOperator *E) { @@ -256,6 +353,13 @@ class ScalarExprEmitter : public StmtVisitor { llvm_unreachable("NYI"); } + mlir::Value buildUnaryOp(const UnaryOperator *E, mlir::cir::UnaryOpKind kind, + mlir::Value input) { + return Builder.create( + CGF.getLoc(E->getSourceRange().getBegin()), + CGF.getCIRType(E->getType()), kind, input); + } + // C++ mlir::Value VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { llvm_unreachable("NYI"); diff --git a/clang/test/CIR/CodeGen/inc-dec.cpp b/clang/test/CIR/CodeGen/inc-dec.cpp new file mode 100644 index 000000000000..9803e4e9ebb9 --- /dev/null +++ b/clang/test/CIR/CodeGen/inc-dec.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fenable-clangir -Wno-unused-value -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +unsigned id0() { + unsigned a = 1; + return ++a; +} + +// CHECK: cir.func @_Z3id0v() -> i32 { +// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr , ["__retval", uninitialized] +// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr , ["a", cinit] +// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]] +// CHECK: %[[#AFTER_A:]] = cir.unary(inc, %[[#BEFORE_A]]) +// CHECK: cir.store %[[#AFTER_A]], %[[#A]] +// CHECK: cir.store %[[#AFTER_A]], %[[#RET]] + + +unsigned id1() { + unsigned a = 1; + return --a; +} + +// CHECK: cir.func @_Z3id1v() -> i32 { +// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr , ["__retval", uninitialized] +// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr , ["a", cinit] +// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]] +// CHECK: %[[#AFTER_A:]] = cir.unary(dec, %[[#BEFORE_A]]) +// CHECK: cir.store %[[#AFTER_A]], %[[#A]] +// CHECK: cir.store %[[#AFTER_A]], %[[#RET]] + +unsigned id2() { + unsigned a = 1; + return a++; +} + +// CHECK: cir.func @_Z3id2v() -> i32 { +// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr , ["__retval", uninitialized] +// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr , ["a", cinit] +// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]] +// CHECK: %[[#AFTER_A:]] = cir.unary(inc, %[[#BEFORE_A]]) +// CHECK: cir.store %[[#AFTER_A]], %[[#A]] +// CHECK: cir.store %[[#BEFORE_A]], %[[#RET]] + +unsigned id3() { + unsigned a = 1; + return a--; +} + +// CHECK: cir.func @_Z3id3v() -> i32 { +// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr , ["__retval", uninitialized] +// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr , ["a", cinit] +// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]] +// CHECK: %[[#AFTER_A:]] = cir.unary(dec, %[[#BEFORE_A]]) +// CHECK: cir.store %[[#AFTER_A]], %[[#A]] +// CHECK: cir.store %[[#BEFORE_A]], %[[#RET]] From 9a376d7b3f4b09cdec0528a9e34e00feb28e19cf Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Thu, 22 Sep 2022 23:38:16 -0700 Subject: [PATCH 3/3] [CIR][CodeGen] Basic lowering of increment/decrement --- clang/lib/CIR/CodeGen/LowerToLLVM.cpp | 35 ++++++++++++++++++++-- clang/test/CIR/CIRToLLVM/unary-inc-dec.cir | 29 ++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/CIRToLLVM/unary-inc-dec.cir diff --git a/clang/lib/CIR/CodeGen/LowerToLLVM.cpp b/clang/lib/CIR/CodeGen/LowerToLLVM.cpp index 3e726c5bd3b8..f2ade8973c91 100644 --- a/clang/lib/CIR/CodeGen/LowerToLLVM.cpp +++ b/clang/lib/CIR/CodeGen/LowerToLLVM.cpp @@ -206,6 +206,37 @@ class CIRFuncLowering : public mlir::OpRewritePattern { } }; +class CIRUnaryOpLowering : public mlir::OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + mlir::LogicalResult + matchAndRewrite(mlir::cir::UnaryOp op, + mlir::PatternRewriter &rewriter) const override { + mlir::Type type = op.getInput().getType(); + assert(type.isa() && "operand type not supported yet"); + + switch (op.getKind()) { + case mlir::cir::UnaryOpKind::Inc: { + auto One = rewriter.create( + op.getLoc(), type, mlir::IntegerAttr::get(type, 1)); + rewriter.replaceOpWithNewOp(op, op.getType(), + op.getInput(), One); + break; + } + case mlir::cir::UnaryOpKind::Dec: { + auto One = rewriter.create( + op.getLoc(), type, mlir::IntegerAttr::get(type, 1)); + rewriter.replaceOpWithNewOp(op, op.getType(), + op.getInput(), One); + break; + } + } + + return mlir::LogicalResult::success(); + } +}; + class CIRBinOpLowering : public mlir::OpRewritePattern { public: using OpRewritePattern::OpRewritePattern; @@ -447,8 +478,8 @@ class CIRBrOpLowering : public mlir::OpRewritePattern { void populateCIRToMemRefConversionPatterns(mlir::RewritePatternSet &patterns) { patterns.add(patterns.getContext()); + CIRConstantLowering, CIRUnaryOpLowering, CIRBinOpLowering, + CIRCmpOpLowering, CIRBrOpLowering>(patterns.getContext()); } void ConvertCIRToLLVMPass::runOnOperation() { diff --git a/clang/test/CIR/CIRToLLVM/unary-inc-dec.cir b/clang/test/CIR/CIRToLLVM/unary-inc-dec.cir new file mode 100644 index 000000000000..30384fdfef50 --- /dev/null +++ b/clang/test/CIR/CIRToLLVM/unary-inc-dec.cir @@ -0,0 +1,29 @@ +// RUN: cir-tool %s -cir-to-func -cir-to-memref -o - | FileCheck %s -check-prefix=MLIR +// RUN: cir-tool %s -cir-to-func -cir-to-memref -cir-to-llvm -o - | mlir-translate -mlir-to-llvmir | FileCheck %s -check-prefix=LLVM + +module { + cir.func @foo() { + %0 = cir.alloca i32, cir.ptr , ["a", cinit] {alignment = 4 : i64} + %1 = cir.alloca i32, cir.ptr , ["b", cinit] {alignment = 4 : i64} + %2 = cir.cst(2 : i32) : i32 + cir.store %2, %0 : i32, cir.ptr + cir.store %2, %1 : i32, cir.ptr + + %3 = cir.load %0 : cir.ptr , i32 + %4 = cir.unary(inc, %3) : i32, i32 + cir.store %4, %0 : i32, cir.ptr + + %5 = cir.load %1 : cir.ptr , i32 + %6 = cir.unary(dec, %5) : i32, i32 + cir.store %6, %1 : i32, cir.ptr + cir.return + } +} + +// MLIR: = arith.constant 1 +// MLIR: = arith.addi +// MLIR: = arith.constant 1 +// MLIR: = arith.subi + +// LLVM: = add i32 %[[#]], 1 +// LLVM: = sub i32 %[[#]], 1