Thanks to reviews from Uday and River, this is now landed. It diverges from llvm.switch in a couple other ways now, allowing any signless int to switch on and placing the default case within the list of cases with an explicit keyword:
switch %flag : i32, [
default: ^bb1(%a : i32),
42: ^bb1(%b : i32),
43: ^bb3(%c : i32)
]
For scf.switch, I propose we make it more general, adopting Mehdi and Uday’s suggestions above.
%x, %y = scf.switch %flag : !mydialect.string -> (f32, f32) [
default: {
%x_default = ...
%y_default = ...
%scf.yield %x_default, %y_default
},
"foo": {
%x_0 = ...
%y_0 = ...
%scf.yield %x_0, %y_0
}
"bar", "baz": {
%x_m1 = ...
%y_m1 = ...
%scf.yield %x_m1, %y_m1
}
]
which would be a definition like:
def IfOp : SCF_Op<"switch",
[DeclareOpInterfaceMethods<RegionBranchOpInterface>,
SingleBlockImplicitTerminator<"scf::YieldOp">, RecursiveSideEffects,
NoRegionArguments]> {
let arguments = (ins AnyType:$flag
OptionalAttr<ArrayAttr>:$case_values,
OptionalAttr<I32ElementsAttr>:$case_value_offsets);
let results = (outs Variadic<AnyType>:$results);
let regions = (region SizedRegion<1>:$defaultRegion,
Variadic<SizedRegion<1>>:$caseRegions);
// ...
}
Some particular points:
- the
defaultkeyword will always be on its own. I don’t think there’s any reason we should allow a declared case value to share a region with the default case. It would never make sense to construct such an op statically and if identifying matching regions in a canonicalization, we would just delete them. - We can use offsets to group the case values (as with operands in the llvm and std switch). We could also make this an array of arrays
def 2DArrayAttr : TypedArrayAttrBase<ArrayAttr, "array of arrays">. Not sure which will end up being more ergonomic.
PS: discourse syntax highlighting of the basic block args there is weird. I don’t suppose we can fix that?