diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 29d8aea8d08e..99615442d28d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -146,6 +146,18 @@ def UndefAttr : CIR_TypedAttr<"Undef", "undef"> { }]; } +//===----------------------------------------------------------------------===// +// PoisonAttr +//===----------------------------------------------------------------------===// + +def CIR_PoisonAttr : CIR_TypedAttr<"Poison", "poison"> { + let summary = "Represent a typed poison constant"; + let description = [{ + The PoisonAttr represents a typed poison constant, corresponding to LLVM's + notion of poison. + }]; +} + //===----------------------------------------------------------------------===// // IntegerAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index d17c85f89d58..e1a5c3d9ca33 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -287,7 +287,6 @@ struct MissingFeatures { // Future CIR attributes static bool optInfoAttr() { return false; } - static bool poisonAttr() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 9c36a43c663c..2213c75b404c 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -339,7 +339,7 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, } if (mlir::isa(attrType)) + cir::ConstComplexAttr, cir::PoisonAttr>(attrType)) return success(); assert(isa(attrType) && "What else could we be looking at here?"); @@ -629,6 +629,11 @@ static Value tryFoldCastChain(cir::CastOp op) { } OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) { + if (mlir::isa_and_present(adaptor.getSrc())) { + // Propagate poison value + return cir::PoisonAttr::get(getContext(), getType()); + } + if (getSrc().getType() == getType()) { switch (getKind()) { case cir::CastKind::integral: { @@ -1783,6 +1788,12 @@ static bool isBoolNot(cir::UnaryOp op) { // // and the argument of the first one (%0) will be used instead. OpFoldResult cir::UnaryOp::fold(FoldAdaptor adaptor) { + if (auto poison = + mlir::dyn_cast_if_present(adaptor.getInput())) { + // Propagate poison values + return poison; + } + if (isBoolNot(*this)) if (auto previous = dyn_cast_or_null(getInput().getDefiningOp())) if (isBoolNot(previous)) @@ -2239,16 +2250,18 @@ static OpFoldResult foldUnaryBitOp(mlir::Attribute inputAttr, llvm::function_ref func, bool poisonZero = false) { + if (mlir::isa_and_present(inputAttr)) { + // Propagate poison value + return inputAttr; + } + auto input = mlir::dyn_cast_if_present(inputAttr); if (!input) return nullptr; llvm::APInt inputValue = input.getValue(); - if (poisonZero && inputValue.isZero()) { - // TODO(cir): maybe we should return a poison value here? - assert(!MissingFeatures::poisonAttr()); - return nullptr; - } + if (poisonZero && inputValue.isZero()) + return cir::PoisonAttr::get(input.getType()); llvm::APInt resultValue = func(inputValue); return IntAttr::get(input.getType(), resultValue); @@ -2307,6 +2320,12 @@ OpFoldResult ByteSwapOp::fold(FoldAdaptor adaptor) { } OpFoldResult RotateOp::fold(FoldAdaptor adaptor) { + if (mlir::isa_and_present(adaptor.getInput()) || + mlir::isa_and_present(adaptor.getAmount())) { + // Propagate poison values + return cir::PoisonAttr::get(getType()); + } + auto input = mlir::dyn_cast_if_present(adaptor.getInput()); auto amount = mlir::dyn_cast_if_present(adaptor.getAmount()); if (!input && !amount) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 3cd7de0a56bc..c27b8899ba59 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1027,6 +1027,12 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( mlir::ConversionPatternRewriter &rewriter) const { mlir::Attribute attr = op.getValue(); + if (mlir::isa(attr)) { + rewriter.replaceOpWithNewOp( + op, getTypeConverter()->convertType(op.getType())); + return mlir::success(); + } + if (mlir::isa(op.getType())) { // Verified cir.const operations cannot actually be of these types, but the // lowering pass may generate temporary cir.const operations with these diff --git a/clang/test/CIR/Lowering/poison.cir b/clang/test/CIR/Lowering/poison.cir new file mode 100644 index 000000000000..6f8b7927e3f8 --- /dev/null +++ b/clang/test/CIR/Lowering/poison.cir @@ -0,0 +1,14 @@ +// RUN: cir-translate -cir-to-llvmir --disable-cc-lowering -o %t.ll %s +// RUN: FileCheck -check-prefix=LLVM --input-file=%t.ll %s + +!s32i = !cir.int + +module { + cir.func @lower_poison() -> !s32i { + %0 = cir.const #cir.poison : !s32i + cir.return %0 : !s32i + } + // LLVM-LABEL: @lower_poison + // LLVM-NEXT: ret i32 poison + // LLVM-NEXT: } +} diff --git a/clang/test/CIR/Transforms/bit.cir b/clang/test/CIR/Transforms/bit.cir index c85b05de4d9e..fc27adbc9bb8 100644 --- a/clang/test/CIR/Transforms/bit.cir +++ b/clang/test/CIR/Transforms/bit.cir @@ -25,6 +25,26 @@ module { // CHECK-NEXT: cir.return %[[R]] : !u32i // CHECK-NEXT: } + cir.func @fold_clz_zero_poison() -> !u32i { + %0 = cir.const #cir.int<0> : !u32i + %1 = cir.clz %0 poison_zero : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_clz_zero_poison + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.poison : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_clz_zero_no_poison() -> !u32i { + %0 = cir.const #cir.int<0> : !u32i + %1 = cir.clz %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_clz_zero_no_poison + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<32> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + cir.func @fold_ctz() -> !u32i { %0 = cir.const #cir.int<2> : !u32i %1 = cir.ctz %0 : !u32i @@ -35,6 +55,26 @@ module { // CHECK-NEXT: cir.return %[[R]] : !u32i // CHECK-NEXT: } + cir.func @fold_ctz_zero_poison() -> !u32i { + %0 = cir.const #cir.int<0> : !u32i + %1 = cir.ctz %0 poison_zero : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_ctz_zero_poison + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.poison : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_ctz_zero_no_poison() -> !u32i { + %0 = cir.const #cir.int<0> : !u32i + %1 = cir.ctz %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_ctz_zero_no_poison + // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<32> : !u32i + // CHECK-NEXT: cir.return %[[R]] : !u32i + // CHECK-NEXT: } + cir.func @fold_parity() -> !u32i { // 0xdeadbeef is 0b1101_1110_1010_1101_1011_1110_1110_1111 // 0xdeadbeef contains 24 ones @@ -82,6 +122,16 @@ module { // CHECK-NEXT: cir.return %[[R]] : !u32i // CHECK-NEXT: } + cir.func @fold_input_poison() -> !s32i { + %0 = cir.const #cir.poison : !s32i + %1 = cir.clrsb %0 : !s32i + cir.return %1 : !s32i + } + // CHECK-LABEL: @fold_input_poison + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !s32i + // CHECK-NEXT: cir.return %[[P]] : !s32i + // CHECK-NEXT: } + cir.func @fold_rotate_input_all_zeros(%arg0 : !u32i) -> !u32i { %0 = cir.const #cir.int<0> : !u32i %1 = cir.rotate left %0, %arg0 : !u32i @@ -138,4 +188,24 @@ module { // CHECK-NEXT: %[[R:.+]] = cir.const #cir.int<4024348094> : !u32i // CHECK-NEXT: cir.return %[[R]] : !u32i // CHECK-NEXT: } + + cir.func @fold_rotate_input_poison(%arg0 : !u32i) -> !u32i { + %0 = cir.const #cir.poison : !u32i + %1 = cir.rotate left %0, %arg0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_rotate_input_poison + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !u32i + // CHECK-NEXT: cir.return %[[P]] : !u32i + // CHECK-NEXT: } + + cir.func @fold_rotate_amount_poison(%arg0 : !u32i) -> !u32i { + %0 = cir.const #cir.poison : !u32i + %1 = cir.rotate left %arg0, %0 : !u32i + cir.return %1 : !u32i + } + // CHECK-LABEL: @fold_rotate_amount_poison + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !u32i + // CHECK-NEXT: cir.return %[[P]] : !u32i + // CHECK-NEXT: } } diff --git a/clang/test/CIR/Transforms/canonicalize.cir b/clang/test/CIR/Transforms/canonicalize.cir index 7ba163eb30bb..5daff119a626 100644 --- a/clang/test/CIR/Transforms/canonicalize.cir +++ b/clang/test/CIR/Transforms/canonicalize.cir @@ -39,6 +39,16 @@ module { // CHECK: cir.func{{.*}} @unary_not(%arg0: !cir.bool) -> !cir.bool // CHECK-NEXT: cir.return %arg0 : !cir.bool + cir.func @unary_poison() -> !s32i { + %0 = cir.const #cir.poison : !s32i + %1 = cir.unary(inc, %0) : !s32i, !s32i + cir.return %1 : !s32i + } + // CHECK: @unary_poison + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !s32i + // CHECK-NEXT: cir.return %[[P]] : !s32i + // CHECK-NEXT: } + cir.func @cast1(%arg0: !cir.bool) -> !cir.bool { %0 = cir.cast(bool_to_int, %arg0 : !cir.bool), !s32i %1 = cir.cast(int_to_bool, %0 : !s32i), !cir.bool @@ -70,4 +80,14 @@ module { // CHECK-NEXT: %[[CAST3:.*]] = cir.cast(integral, %[[CAST2]] : !s32i), !s64i // CHECK-NEXT: cir.return %[[CAST3]] : !s64i + cir.func @cast_poison() -> !s64i { + %0 = cir.const #cir.poison : !s32i + %1 = cir.cast(integral, %0 : !s32i), !s64i + cir.return %1 : !s64i + } + // CHECK: @cast_poison + // CHECK-NEXT: %[[P:.+]] = cir.const #cir.poison : !s64i + // CHECK-NEXT: cir.return %[[P]] : !s64i + // CHECK-NEXT: } + }