mirror of
https://github.com/intel/llvm.git
synced 2026-02-03 02:26:27 +08:00
[CIR] Add poison attribute (#150760)
This patch adds the `#cir.poison` attribute which represents a poison value. This patch also updates various operation folders to let them propagate poison values from their inputs to their outputs.
This commit is contained in:
@@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -287,7 +287,6 @@ struct MissingFeatures {
|
||||
|
||||
// Future CIR attributes
|
||||
static bool optInfoAttr() { return false; }
|
||||
static bool poisonAttr() { return false; }
|
||||
};
|
||||
|
||||
} // namespace cir
|
||||
|
||||
@@ -339,7 +339,7 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
|
||||
}
|
||||
|
||||
if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
|
||||
cir::ConstComplexAttr>(attrType))
|
||||
cir::ConstComplexAttr, cir::PoisonAttr>(attrType))
|
||||
return success();
|
||||
|
||||
assert(isa<TypedAttr>(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<cir::PoisonAttr>(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<cir::PoisonAttr>(adaptor.getInput())) {
|
||||
// Propagate poison values
|
||||
return poison;
|
||||
}
|
||||
|
||||
if (isBoolNot(*this))
|
||||
if (auto previous = dyn_cast_or_null<UnaryOp>(getInput().getDefiningOp()))
|
||||
if (isBoolNot(previous))
|
||||
@@ -2239,16 +2250,18 @@ static OpFoldResult
|
||||
foldUnaryBitOp(mlir::Attribute inputAttr,
|
||||
llvm::function_ref<llvm::APInt(const llvm::APInt &)> func,
|
||||
bool poisonZero = false) {
|
||||
if (mlir::isa_and_present<cir::PoisonAttr>(inputAttr)) {
|
||||
// Propagate poison value
|
||||
return inputAttr;
|
||||
}
|
||||
|
||||
auto input = mlir::dyn_cast_if_present<IntAttr>(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<cir::PoisonAttr>(adaptor.getInput()) ||
|
||||
mlir::isa_and_present<cir::PoisonAttr>(adaptor.getAmount())) {
|
||||
// Propagate poison values
|
||||
return cir::PoisonAttr::get(getType());
|
||||
}
|
||||
|
||||
auto input = mlir::dyn_cast_if_present<IntAttr>(adaptor.getInput());
|
||||
auto amount = mlir::dyn_cast_if_present<IntAttr>(adaptor.getAmount());
|
||||
if (!input && !amount)
|
||||
|
||||
@@ -1027,6 +1027,12 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
mlir::Attribute attr = op.getValue();
|
||||
|
||||
if (mlir::isa<cir::PoisonAttr>(attr)) {
|
||||
rewriter.replaceOpWithNewOp<mlir::LLVM::PoisonOp>(
|
||||
op, getTypeConverter()->convertType(op.getType()));
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
if (mlir::isa<mlir::IntegerType>(op.getType())) {
|
||||
// Verified cir.const operations cannot actually be of these types, but the
|
||||
// lowering pass may generate temporary cir.const operations with these
|
||||
|
||||
14
clang/test/CIR/Lowering/poison.cir
Normal file
14
clang/test/CIR/Lowering/poison.cir
Normal file
@@ -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<s, 32>
|
||||
|
||||
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: }
|
||||
}
|
||||
@@ -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: }
|
||||
}
|
||||
|
||||
@@ -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: }
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user