[CIR] Add fenv attribute to compare operations (#213072)
This adds the #cir.fenv attribute to the cir.cmp and cir.vec.cmp and adds verfiers to enforce that the attribute is only present when the comparison involves floating-point values. The cir.vec.cmp operation has an existing folder which is skipped in this change when the fenv attribute is present. In most circumstance, this folding could still be done, but the implementation is intentionally conservative at this point. Proper checking of the conditions under which folding would be legal will be added later. The cir.cmp operation does not currently have a folder. Assisted-by: Cursor / various models
This commit is contained in:
parent
4f705acc38
commit
005b3858f6
6 changed files with 180 additions and 5 deletions
|
|
@ -2500,7 +2500,8 @@ def CIR_CmpOpKind : CIR_I32EnumAttr<"CmpOpKind", "compare operation kind", [
|
|||
I32EnumAttrCase<"uno", 7>
|
||||
]>;
|
||||
|
||||
def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
|
||||
def CIR_CmpOp : CIR_Op<"cmp",
|
||||
!listconcat([SameTypeOperands], CIR_FenvOpTraits)> {
|
||||
let summary = "Compare two values and produce a boolean result";
|
||||
let description = [{
|
||||
The `cir.cmp` operation compares two operands of the same type and produces
|
||||
|
|
@ -2524,16 +2525,22 @@ def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
|
|||
The `one` and `uno` predicates are floating-point specific: `one` is
|
||||
ordered not-equal (false for NaN), `uno` tests if either operand is NaN.
|
||||
|
||||
The optional `fenv` attribute describes constraints on the floating-point
|
||||
handling of the operation. It is only valid for floating-point
|
||||
comparisons.
|
||||
|
||||
```
|
||||
%0 = cir.cmp gt %1, %2 : !s32i
|
||||
%1 = cir.cmp eq %a, %b : !cir.ptr<!u8i>
|
||||
%2 = cir.cmp lt %x, %y : !cir.float {fenv = #cir.fenv<>}
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
CIR_CmpOpKind:$kind,
|
||||
CIR_ComparableType:$lhs,
|
||||
CIR_ComparableType:$rhs
|
||||
CIR_ComparableType:$rhs,
|
||||
OptionalAttr<CIR_FenvAttr>:$fenv
|
||||
);
|
||||
|
||||
let results = (outs CIR_BoolType:$result);
|
||||
|
|
@ -2542,6 +2549,18 @@ def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
|
|||
$kind $lhs `,` $rhs `:` type($lhs) attr-dict
|
||||
}];
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "cir::CmpOpKind":$kind, "mlir::Value":$lhs,
|
||||
"mlir::Value":$rhs), [{
|
||||
build($_builder, $_state, kind, lhs, rhs, cir::FenvAttr{});
|
||||
}]>,
|
||||
OpBuilder<(ins "mlir::Type":$result, "cir::CmpOpKind":$kind,
|
||||
"mlir::Value":$lhs, "mlir::Value":$rhs), [{
|
||||
build($_builder, $_state, result, kind, lhs, rhs, cir::FenvAttr{});
|
||||
}]>
|
||||
];
|
||||
|
||||
let hasVerifier = 1;
|
||||
let isLLVMLoweringRecursive = true;
|
||||
let hasCXXABILowering = true;
|
||||
}
|
||||
|
|
@ -5716,7 +5735,8 @@ def CIR_VecExtractOp : CIR_Op<"vec.extract", [
|
|||
// VecCmpOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_VecCmpOp : CIR_Op<"vec.cmp", [Pure, SameTypeOperands]> {
|
||||
def CIR_VecCmpOp : CIR_Op<"vec.cmp",
|
||||
!listconcat([SameTypeOperands], CIR_FenvOpTraits)> {
|
||||
let summary = "Compare two vectors";
|
||||
let description = [{
|
||||
The `cir.vec.cmp` operation does an element-wise comparison of two vectors
|
||||
|
|
@ -5724,16 +5744,24 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp", [Pure, SameTypeOperands]> {
|
|||
whose element type is the signed integral type that is the same size as the
|
||||
element type of the operands. The values in the result are 0 or -1.
|
||||
|
||||
The optional `fenv` attribute describes constraints on the floating-point
|
||||
handling of the operation. It is only valid for floating-point vector
|
||||
comparisons.
|
||||
|
||||
```
|
||||
%eq = cir.vec.cmp(eq, %vec_a, %vec_b) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
|
||||
%lt = cir.vec.cmp(lt, %vec_a, %vec_b) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
|
||||
%gt = cir.vec.cmp(gt, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> {
|
||||
fenv = #cir.fenv<>
|
||||
}
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
CIR_CmpOpKind:$kind,
|
||||
CIR_VectorType:$lhs,
|
||||
CIR_VectorType:$rhs
|
||||
CIR_VectorType:$rhs,
|
||||
OptionalAttr<CIR_FenvAttr>:$fenv
|
||||
);
|
||||
|
||||
let results = (outs CIR_VectorType:$result);
|
||||
|
|
@ -5743,6 +5771,14 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp", [Pure, SameTypeOperands]> {
|
|||
qualified(type($result)) attr-dict
|
||||
}];
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "mlir::Type":$result, "cir::CmpOpKind":$kind,
|
||||
"mlir::Value":$lhs, "mlir::Value":$rhs), [{
|
||||
build($_builder, $_state, result, kind, lhs, rhs, cir::FenvAttr{});
|
||||
}]>
|
||||
];
|
||||
|
||||
let hasVerifier = 1;
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3536,11 +3536,37 @@ OpFoldResult cir::VecExtractOp::fold(FoldAdaptor adaptor) {
|
|||
return elements[index];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CmpOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
LogicalResult cir::CmpOp::verify() {
|
||||
if (getFenvAttr() && !cir::isAnyFloatingPointType(getLhs().getType()))
|
||||
return emitOpError()
|
||||
<< "'fenv' is only valid for floating-point comparisons";
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// VecCmpOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
LogicalResult cir::VecCmpOp::verify() {
|
||||
if (getFenvAttr() && !cir::isFPOrVectorOfFPType(getLhs().getType()))
|
||||
return emitOpError()
|
||||
<< "'fenv' is only valid for floating-point comparisons";
|
||||
return success();
|
||||
}
|
||||
|
||||
OpFoldResult cir::VecCmpOp::fold(FoldAdaptor adaptor) {
|
||||
// Do not fold when fenv is present.
|
||||
// TODO(cir): This is overly conservative. We could fold comparisons as long
|
||||
// as we can prove that the operation wouldn't raise exceptions or
|
||||
// when the fenv attribute does not require strict exception
|
||||
// semantics.
|
||||
if (getFenvAttr())
|
||||
return {};
|
||||
|
||||
auto lhsVecAttr =
|
||||
mlir::dyn_cast_if_present<cir::ConstVectorAttr>(adaptor.getLhs());
|
||||
auto rhsVecAttr =
|
||||
|
|
|
|||
|
|
@ -104,3 +104,24 @@ cir.func @cast_fenv(%a: !cir.float, %b: !cir.double, %i: !s32i) {
|
|||
%3 = cir.cast float_to_bool %a : !cir.float -> !cir.bool
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func @cmp_fenv
|
||||
cir.func @cmp_fenv(%a: !cir.float, %b: !cir.float,
|
||||
%va: !cir.vector<4 x !cir.float>,
|
||||
%vb: !cir.vector<4 x !cir.float>) {
|
||||
// CHECK: cir.cmp lt %{{.*}}, %{{.*}} : !cir.float {fenv = #cir.fenv<except_mode = unmasked>}
|
||||
%0 = cir.cmp lt %a, %b : !cir.float {fenv = #cir.fenv<except_mode = unmasked>}
|
||||
// CHECK: cir.cmp eq %{{.*}}, %{{.*}} : !cir.float {fenv = #cir.fenv<>}
|
||||
%1 = cir.cmp eq %a, %b : !cir.float {fenv = #cir.fenv<>}
|
||||
// CHECK: cir.vec.cmp(gt, %{{.*}}, %{{.*}}) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> {fenv = #cir.fenv<strict_except = true>}
|
||||
%2 = cir.vec.cmp(gt, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> {
|
||||
fenv = #cir.fenv<strict_except = true>
|
||||
}
|
||||
// CHECK: cir.cmp ne %{{.*}}, %{{.*}} : !cir.float
|
||||
// CHECK-NOT: fenv
|
||||
%3 = cir.cmp ne %a, %b : !cir.float
|
||||
// CHECK: cir.vec.cmp(le, %{{.*}}, %{{.*}}) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i>
|
||||
// CHECK-NOT: fenv
|
||||
%4 = cir.vec.cmp(le, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i>
|
||||
cir.return
|
||||
}
|
||||
|
|
|
|||
39
clang/test/CIR/IR/invalid-cmp.cir
Normal file
39
clang/test/CIR/IR/invalid-cmp.cir
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// RUN: cir-opt %s -verify-diagnostics -split-input-file
|
||||
|
||||
!s32i = !cir.int<s, 32>
|
||||
|
||||
module {
|
||||
cir.func @fenv_on_int_cmp(%a: !s32i, %b: !s32i) {
|
||||
// expected-error@+1 {{'fenv' is only valid for floating-point comparisons}}
|
||||
%0 = cir.cmp eq %a, %b : !s32i {fenv = #cir.fenv<>}
|
||||
cir.return
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
!s32i = !cir.int<s, 32>
|
||||
|
||||
module {
|
||||
cir.func @fenv_on_ptr_cmp(%a: !cir.ptr<!s32i>, %b: !cir.ptr<!s32i>) {
|
||||
// expected-error@+1 {{'fenv' is only valid for floating-point comparisons}}
|
||||
%0 = cir.cmp ne %a, %b : !cir.ptr<!s32i> {fenv = #cir.fenv<>}
|
||||
cir.return
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
!s32i = !cir.int<s, 32>
|
||||
|
||||
module {
|
||||
cir.func @fenv_on_int_vec_cmp(%a: !cir.vector<4 x !s32i>,
|
||||
%b: !cir.vector<4 x !s32i>) {
|
||||
// expected-error@+1 {{'fenv' is only valid for floating-point comparisons}}
|
||||
%0 = cir.vec.cmp(eq, %a, %b) : !cir.vector<4 x !s32i>,
|
||||
!cir.vector<4 x !s32i> {
|
||||
fenv = #cir.fenv<>
|
||||
}
|
||||
cir.return
|
||||
}
|
||||
}
|
||||
|
|
@ -292,3 +292,34 @@ module {
|
|||
// CHECK-SAME: #cir.int<-1> : !cir.int<s, 1>, #cir.int<-1> : !cir.int<s, 1>]> : !cir.vector<4 x !cir.int<s, 1>>
|
||||
// CHECK-NEXT: cir.return %[[RES]] : !cir.vector<4 x !cir.int<s, 1>>
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
!s32i = !cir.int<s, 32>
|
||||
|
||||
module {
|
||||
cir.func @no_fold_cmp_vector_with_fenv() -> !cir.vector<4 x !s32i> {
|
||||
%vec_1 = cir.const #cir.const_vector<[
|
||||
#cir.fp<0x7FC00000> : !cir.float,
|
||||
#cir.fp<2.000000e+00> : !cir.float,
|
||||
#cir.fp<3.000000e+00> : !cir.float,
|
||||
#cir.fp<4.000000e+00> : !cir.float
|
||||
]> : !cir.vector<4 x !cir.float>
|
||||
%vec_2 = cir.const #cir.const_vector<[
|
||||
#cir.fp<5.000000e+00> : !cir.float,
|
||||
#cir.fp<6.000000e+00> : !cir.float,
|
||||
#cir.fp<7.000000e+00> : !cir.float,
|
||||
#cir.fp<8.000000e+00> : !cir.float
|
||||
]> : !cir.vector<4 x !cir.float>
|
||||
%new_vec = cir.vec.cmp(lt, %vec_1, %vec_2) :
|
||||
!cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> {
|
||||
fenv = #cir.fenv<strict_except = true>
|
||||
}
|
||||
cir.return %new_vec : !cir.vector<4 x !s32i>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func{{.*}} @no_fold_cmp_vector_with_fenv
|
||||
// CHECK: cir.vec.cmp(lt, %{{.*}}, %{{.*}}) :
|
||||
// CHECK-SAME: !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i>
|
||||
// CHECK-SAME: {fenv = #cir.fenv<strict_except = true>}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,9 @@ protected:
|
|||
TEST_F(CIRFenvOpTest, MemoryEffects) {
|
||||
OwningOpRef<ModuleOp> module = parse(R"CIR(
|
||||
!s32i = !cir.int<s, 32>
|
||||
cir.func @f(%a: !cir.float, %b: !cir.float, %c: !cir.float) {
|
||||
cir.func @f(%a: !cir.float, %b: !cir.float, %c: !cir.float,
|
||||
%va: !cir.vector<4 x !cir.float>,
|
||||
%vb: !cir.vector<4 x !cir.float>) {
|
||||
%0 = cir.fadd %a, %b : !cir.float
|
||||
%1 = cir.fadd %a, %b : !cir.float {fenv = #cir.fenv<>}
|
||||
%2 = cir.sqrt %a : !cir.float {fenv = #cir.fenv<>}
|
||||
|
|
@ -75,6 +77,12 @@ TEST_F(CIRFenvOpTest, MemoryEffects) {
|
|||
%7 = cir.lround %a : !cir.float -> !s32i {fenv = #cir.fenv<>}
|
||||
%8 = cir.cast floating %a : !cir.float -> !cir.double
|
||||
%9 = cir.cast floating %a : !cir.float -> !cir.double {fenv = #cir.fenv<>}
|
||||
%10 = cir.cmp lt %a, %b : !cir.float
|
||||
%11 = cir.cmp lt %a, %b : !cir.float {fenv = #cir.fenv<>}
|
||||
%12 = cir.vec.cmp(eq, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i>
|
||||
%13 = cir.vec.cmp(eq, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> {
|
||||
fenv = #cir.fenv<>
|
||||
}
|
||||
cir.return
|
||||
}
|
||||
)CIR");
|
||||
|
|
@ -115,6 +123,20 @@ TEST_F(CIRFenvOpTest, MemoryEffects) {
|
|||
EXPECT_TRUE(isMemoryEffectFree(castOps[0]));
|
||||
expectFenvReadAndWrite(castOps[1]);
|
||||
EXPECT_FALSE(isMemoryEffectFree(castOps[1]));
|
||||
|
||||
SmallVector<cir::CmpOp> cmpOps = findOps<cir::CmpOp>(*module);
|
||||
ASSERT_EQ(cmpOps.size(), 2u);
|
||||
EXPECT_TRUE(getEffects(cmpOps[0]).empty());
|
||||
EXPECT_TRUE(isMemoryEffectFree(cmpOps[0]));
|
||||
expectFenvReadAndWrite(cmpOps[1]);
|
||||
EXPECT_FALSE(isMemoryEffectFree(cmpOps[1]));
|
||||
|
||||
SmallVector<cir::VecCmpOp> vecCmpOps = findOps<cir::VecCmpOp>(*module);
|
||||
ASSERT_EQ(vecCmpOps.size(), 2u);
|
||||
EXPECT_TRUE(getEffects(vecCmpOps[0]).empty());
|
||||
EXPECT_TRUE(isMemoryEffectFree(vecCmpOps[0]));
|
||||
expectFenvReadAndWrite(vecCmpOps[1]);
|
||||
EXPECT_FALSE(isMemoryEffectFree(vecCmpOps[1]));
|
||||
}
|
||||
|
||||
TEST_F(CIRFenvOpTest, Speculatability) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue