-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[CIR] Upstream TernaryOp #137184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CIR] Upstream TernaryOp #137184
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -610,9 +610,9 @@ def ConditionOp : CIR_Op<"condition", [ | |
//===----------------------------------------------------------------------===// | ||
|
||
def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator, | ||
ParentOneOf<["IfOp", "ScopeOp", "SwitchOp", | ||
"WhileOp", "ForOp", "CaseOp", | ||
"DoWhileOp"]>]> { | ||
ParentOneOf<["CaseOp", "DoWhileOp", "ForOp", | ||
"IfOp", "ScopeOp", "SwitchOp", | ||
"TernaryOp", "WhileOp"]>]> { | ||
let summary = "Represents the default branching behaviour of a region"; | ||
let description = [{ | ||
The `cir.yield` operation terminates regions on different CIR operations, | ||
|
@@ -1462,6 +1462,63 @@ def SelectOp : CIR_Op<"select", [Pure, | |
}]; | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// TernaryOp | ||
//===----------------------------------------------------------------------===// | ||
|
||
def TernaryOp : CIR_Op<"ternary", | ||
[DeclareOpInterfaceMethods<RegionBranchOpInterface>, | ||
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments]> { | ||
let summary = "The `cond ? a : b` C/C++ ternary operation"; | ||
let description = [{ | ||
The `cir.ternary` operation represents C/C++ ternary, much like a `select` | ||
operation. The first argument is a `cir.bool` condition to evaluate, followed | ||
by two regions to execute (true or false). This is different from `cir.if` | ||
since each region is one block sized and the `cir.yield` closing the block | ||
scope should have one argument. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we represent the GNU extension of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GNU There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we document it here? One of the 'features' of it IIRC is that it only evaluates |
||
|
||
`cir.ternary` also represents the GNU binary conditional operator ?: which | ||
reuses the parent operation for both the condition and the true branch to | ||
evaluate it only once. | ||
|
||
Example: | ||
|
||
```mlir | ||
// cond = a && b; | ||
|
||
%x = cir.ternary (%cond, true_region { | ||
... | ||
cir.yield %a : i32 | ||
}, false_region { | ||
... | ||
cir.yield %b : i32 | ||
}) -> i32 | ||
``` | ||
}]; | ||
let arguments = (ins CIR_BoolType:$cond); | ||
let regions = (region AnyRegion:$trueRegion, | ||
AnyRegion:$falseRegion); | ||
let results = (outs Optional<CIR_AnyType>:$result); | ||
mmha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
let skipDefaultBuilders = 1; | ||
let builders = [ | ||
OpBuilder<(ins "mlir::Value":$cond, | ||
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$trueBuilder, | ||
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$falseBuilder) | ||
> | ||
]; | ||
|
||
// All constraints already verified elsewhere. | ||
let hasVerifier = 0; | ||
|
||
let assemblyFormat = [{ | ||
`(` $cond `,` | ||
`true` $trueRegion `,` | ||
`false` $falseRegion | ||
`)` `:` functional-type(operands, results) attr-dict | ||
}]; | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// GlobalOp | ||
//===----------------------------------------------------------------------===// | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// RUN: cir-opt %s | cir-opt | FileCheck %s | ||
!u32i = !cir.int<u, 32> | ||
|
||
module { | ||
cir.func @blue(%arg0: !cir.bool) -> !u32i { | ||
%0 = cir.ternary(%arg0, true { | ||
%a = cir.const #cir.int<0> : !u32i | ||
cir.yield %a : !u32i | ||
}, false { | ||
%b = cir.const #cir.int<1> : !u32i | ||
cir.yield %b : !u32i | ||
}) : (!cir.bool) -> !u32i | ||
cir.return %0 : !u32i | ||
} | ||
} | ||
|
||
// CHECK: module { | ||
|
||
// CHECK: cir.func @blue(%arg0: !cir.bool) -> !u32i { | ||
// CHECK: %0 = cir.ternary(%arg0, true { | ||
// CHECK: %1 = cir.const #cir.int<0> : !u32i | ||
// CHECK: cir.yield %1 : !u32i | ||
// CHECK: }, false { | ||
// CHECK: %1 = cir.const #cir.int<1> : !u32i | ||
// CHECK: cir.yield %1 : !u32i | ||
// CHECK: }) : (!cir.bool) -> !u32i | ||
// CHECK: cir.return %0 : !u32i | ||
// CHECK: } | ||
|
||
// CHECK: } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// RUN: cir-translate -cir-to-llvmir --disable-cc-lowering -o %t.ll %s | ||
// RUN: FileCheck --input-file=%t.ll -check-prefix=LLVM %s | ||
|
||
!u32i = !cir.int<u, 32> | ||
|
||
module { | ||
cir.func @blue(%arg0: !cir.bool) -> !u32i { | ||
%0 = cir.ternary(%arg0, true { | ||
%a = cir.const #cir.int<0> : !u32i | ||
cir.yield %a : !u32i | ||
}, false { | ||
%b = cir.const #cir.int<1> : !u32i | ||
cir.yield %b : !u32i | ||
}) : (!cir.bool) -> !u32i | ||
cir.return %0 : !u32i | ||
} | ||
} | ||
|
||
// LLVM-LABEL: define i32 {{.*}}@blue( | ||
// LLVM-SAME: i1 [[PRED:%[[:alnum:]]+]]) | ||
// LLVM: br i1 [[PRED]], label %[[B1:[[:alnum:]]+]], label %[[B2:[[:alnum:]]+]] | ||
// LLVM: [[B1]]: | ||
// LLVM: br label %[[M:[[:alnum:]]+]] | ||
// LLVM: [[B2]]: | ||
// LLVM: br label %[[M]] | ||
// LLVM: [[M]]: | ||
// LLVM: [[R:%[[:alnum:]]+]] = phi i32 [ 1, %[[B2]] ], [ 0, %[[B1]] ] | ||
// LLVM: br label %[[B3:[[:alnum:]]+]] | ||
// LLVM: [[B3]]: | ||
// LLVM: ret i32 [[R]] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// RUN: cir-opt %s -cir-flatten-cfg -o - | FileCheck %s | ||
|
||
!s32i = !cir.int<s, 32> | ||
|
||
module { | ||
cir.func @foo(%arg0: !s32i) -> !s32i { | ||
%0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["y", init] {alignment = 4 : i64} | ||
%1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64} | ||
cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i> | ||
%2 = cir.load %0 : !cir.ptr<!s32i>, !s32i | ||
%3 = cir.const #cir.int<0> : !s32i | ||
%4 = cir.cmp(gt, %2, %3) : !s32i, !cir.bool | ||
%5 = cir.ternary(%4, true { | ||
%7 = cir.const #cir.int<3> : !s32i | ||
cir.yield %7 : !s32i | ||
}, false { | ||
%7 = cir.const #cir.int<5> : !s32i | ||
cir.yield %7 : !s32i | ||
}) : (!cir.bool) -> !s32i | ||
cir.store %5, %1 : !s32i, !cir.ptr<!s32i> | ||
%6 = cir.load %1 : !cir.ptr<!s32i>, !s32i | ||
cir.return %6 : !s32i | ||
} | ||
|
||
// CHECK: cir.func @foo(%arg0: !s32i) -> !s32i { | ||
// CHECK: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["y", init] {alignment = 4 : i64} | ||
// CHECK: %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64} | ||
// CHECK: cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i> | ||
// CHECK: %2 = cir.load %0 : !cir.ptr<!s32i>, !s32i | ||
// CHECK: %3 = cir.const #cir.int<0> : !s32i | ||
// CHECK: %4 = cir.cmp(gt, %2, %3) : !s32i, !cir.bool | ||
// CHECK: cir.brcond %4 ^bb1, ^bb2 | ||
// CHECK: ^bb1: // pred: ^bb0 | ||
// CHECK: %5 = cir.const #cir.int<3> : !s32i | ||
// CHECK: cir.br ^bb3(%5 : !s32i) | ||
// CHECK: ^bb2: // pred: ^bb0 | ||
// CHECK: %6 = cir.const #cir.int<5> : !s32i | ||
// CHECK: cir.br ^bb3(%6 : !s32i) | ||
// CHECK: ^bb3(%7: !s32i): // 2 preds: ^bb1, ^bb2 | ||
// CHECK: cir.br ^bb4 | ||
// CHECK: ^bb4: // pred: ^bb3 | ||
// CHECK: cir.store %7, %1 : !s32i, !cir.ptr<!s32i> | ||
// CHECK: %8 = cir.load %1 : !cir.ptr<!s32i>, !s32i | ||
// CHECK: cir.return %8 : !s32i | ||
// CHECK: } | ||
|
||
cir.func @foo2(%arg0: !cir.bool) { | ||
cir.ternary(%arg0, true { | ||
cir.yield | ||
}, false { | ||
cir.yield | ||
}) : (!cir.bool) -> () | ||
cir.return | ||
} | ||
|
||
// CHECK: cir.func @foo2(%arg0: !cir.bool) { | ||
// CHECK: cir.brcond %arg0 ^bb1, ^bb2 | ||
// CHECK: ^bb1: // pred: ^bb0 | ||
// CHECK: cir.br ^bb3 | ||
// CHECK: ^bb2: // pred: ^bb0 | ||
// CHECK: cir.br ^bb3 | ||
// CHECK: ^bb3: // 2 preds: ^bb1, ^bb2 | ||
// CHECK: cir.br ^bb4 | ||
// CHECK: ^bb4: // pred: ^bb3 | ||
// CHECK: cir.return | ||
// CHECK: } | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. At least in the incubator, we use
cir.select
to implement the ternary operator.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We simplify
cir.ternary
tocir.select
in FlattenCFG. See the tests here. Without-O1
the first test case gets represented bycir.ternary
: https://godbolt.org/z/fz39rh5KnThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. I didn't realize we ran CIR simplification based on the opt level. I guess it makes sense in a way, but I'm not thrilled with the idea that this is happening in the front end. This relates to the suggestion I made elsewhere that maybe we should be moving the lowering and transforms out of the clang component.