-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[Support][Casting] Add predicates for isa*
functions
#83753
Conversation
@llvm/pr-subscribers-llvm-support Author: Jakub Kuderski (kuhar) ChangesExpose function objects that call into Before this change, llvm::all_of(myTypes,
[](auto type) { return llvm::isa<VectorType>(type); }); With this PR the example above becomes: llvm::all_of(myTypes, llvm::IsaPred<VectorType>); As an alternative solution, I considered redefining Full diff: https://github.com/llvm/llvm-project/pull/83753.diff 2 Files Affected:
diff --git a/llvm/include/llvm/Support/Casting.h b/llvm/include/llvm/Support/Casting.h
index 2391c1a531a5be..4c0ae59b1aa986 100644
--- a/llvm/include/llvm/Support/Casting.h
+++ b/llvm/include/llvm/Support/Casting.h
@@ -801,6 +801,52 @@ template <class X, class Y>
return unique_dyn_cast_or_null<X, Y>(Val);
}
+//===----------------------------------------------------------------------===//
+// Isa Predicates
+//===----------------------------------------------------------------------===//
+
+/// These are wrappers over isa* function that allow them to be used in generic
+/// algorithms such as `llvm:all_of`, `llvm::none_of`, etc. This is accomplished
+/// by exposing the isa* functions through function objects with a generic
+/// function call operator.
+
+namespace detail {
+template <typename... Types> struct IsaCheckPredicate {
+ template <typename T> bool operator()(const T &Val) const {
+ return isa<Types...>(Val);
+ }
+};
+
+template <typename... Types> struct IsaAndPresentCheckPredicate {
+ template <typename T> bool operator()(const T &Val) const {
+ return isa_and_present<Types...>(Val);
+ }
+};
+} // namespace detail
+
+/// Function object wrapper for the `llvm::isa` type check. The function call
+/// operator returns true when the value can be cast to any type in `Types`.
+/// Example:
+/// ```
+/// SmallVector<Type> myTypes = ...;
+/// if (llvm::all_of(myTypes, llvm::IsaPred<VectorType>))
+/// ...
+/// ```
+template <typename... Types>
+inline constexpr detail::IsaCheckPredicate<Types...> IsaPred{};
+
+/// Function object wrapper for the `llvm::isa_and_present` type check. The
+/// function call operator returns true when the value can be cast to any type
+/// in `Types`, or if the value is not present (e.g., nullptr). Example:
+/// ```
+/// SmallVector<Type> myTypes = ...;
+/// if (llvm::all_of(myTypes, llvm::IsaAndPresnetPred<VectorType>))
+/// ...
+/// ```
+template <typename... Types>
+inline constexpr detail::IsaAndPresentCheckPredicate<Types...>
+ IsaAndPresentPred{};
+
} // end namespace llvm
#endif // LLVM_SUPPORT_CASTING_H
diff --git a/llvm/unittests/Support/Casting.cpp b/llvm/unittests/Support/Casting.cpp
index a9256548145986..2f8b0c50e1955c 100644
--- a/llvm/unittests/Support/Casting.cpp
+++ b/llvm/unittests/Support/Casting.cpp
@@ -282,6 +282,20 @@ TEST(CastingTest, dyn_cast_if_present) {
EXPECT_FALSE(t4.hasValue);
}
+TEST(CastingTest, isa_check_predicates) {
+ auto IsaFoo = IsaPred<foo>;
+ auto IsaAndPresentFoo = IsaAndPresentPred<foo>;
+ EXPECT_TRUE(IsaFoo(B1));
+ EXPECT_TRUE(IsaFoo(B2));
+ EXPECT_TRUE(IsaFoo(B3));
+ EXPECT_TRUE(IsaPred<foo>(B4));
+ EXPECT_TRUE((IsaPred<foo, bar>(B4)));
+ EXPECT_TRUE(IsaAndPresentFoo(B2));
+ EXPECT_TRUE(IsaAndPresentFoo(B4));
+ EXPECT_FALSE(IsaAndPresentPred<foo>(fub()));
+ EXPECT_FALSE((IsaAndPresentPred<foo, bar>(fub())));
+}
+
std::unique_ptr<derived> newd() { return std::make_unique<derived>(); }
std::unique_ptr<base> newb() { return std::make_unique<derived>(); }
|
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.
LGTM!
It'd probably be an issue for some callers, but we could make the actual |
I'm worried about the side effects, given that this is probably one of the most commonly used function across the whole project and downstream projects:
|
WDYT @dwblaikie? |
While it's used a lot, I'd hope it's used enough in LLVM that we'd get a pretty good idea of breakage from LLVM, but yeah, probably some novel things in downstream users.
Hmm, not sure I follow this - I'd expect the functor to have one template parameter, so anyone explicitly specifying 2 would fail to compile at least...
Yeah, that's a hard one to argue with... I'm sure there's a bunch of code out there doing unqualified
Oh, sorry I didn't read this the first time through, btw - thanks for considering/noting it. Open to discussion here - but I'm inclined to think the ability specify the second template parameter to isa is a bug/incidental feature of isa being a function template, not a feature & one we could choose not to expose in these predicates (by only providing one template parameter, rather than the parameter pack on the outer functor class)? Like if someone wants to use a specific argument type - they should convert the argument expression themselves, rather than by specifying a second template argument and then relying on impliict conversion of the function parameter? |
@dwblaikie Currently, llvm-project/llvm/include/llvm/Support/Casting.h Lines 547 to 555 in ee044d5
I'm worried that the meaning of auto fPtr = &isa<VectorType, Type>;
...
bool isA = (*fPtr)(ptr); |
Hmm, sorry, not really following... I don't think we'd support multiple target types... it'd have one template parameter, the target type, and specifying 2 would fail to compile. (I think the ability to specify the from type is sort of incidental/accident of the template implementation, not an intentional feature of isa/cast/etc) but it's probably irrelevant, since, as you say, disabling ADL means functor solutions aren't likely ideal/feasible for backwards compatibility reasons.
Still be curious about this ^ but a relatively minor issue. |
@dwblaikie But we do support it today. You can provide one or more target types. Some examples: ➜ grep -rn 'isa<IntegerType, FloatType>' .
./third_party/llvm-project/mlir/lib/IR/Types.cpp:118: return llvm::isa<IntegerType, FloatType>(*this);
./third_party/llvm-project/mlir/lib/Interfaces/DataLayoutInterfaces.cpp:54: if (isa<IntegerType, FloatType>(type))
./third_party/llvm-project/mlir/lib/Interfaces/DataLayoutInterfaces.cpp:627: if (isa<IntegerType, FloatType>(sampleType)) {
./third_party/llvm-project/mlir/lib/Target/LLVMIR/ModuleImport.cpp:662: return isa<IntegerType, FloatType>(type);
./compiler/src/iree/compiler/Dialect/Util/Analysis/Constant/OpOracle.cpp:51: if (llvm::isa<IntegerType, FloatType>(t)) {
./compiler/src/iree/compiler/Codegen/Utils/GPUUtils.cpp:387: assert((llvm::isa<IntegerType, FloatType>(input.getType())) &&
./compiler/src/iree/compiler/Codegen/SPIRV/KernelConfig.cpp:1367: return isa<IntegerType, FloatType>(elementType) && ➜ llvm-project git:(4df364bc93af) grep -rn 'isa<.*Type, .*Type>' . | wc -l
106 If this wasn't allowed, I'd aggrege there's no possibility for confusion.
I have similar feeling, but because this is more like an 'accidental bug in C++', I'd prefer to not to apply it extremely selectively to 2 functions in the whole project. To summarize, because I've spent probably days/weeks an various llvm integration rotations, I'm sympathetic to downstream maintainers and would like to avoid potential breakage. The ADL thing + function pointer template argument behavior change would make me uncomfortable redefining a very core API. This would deserve a wider discussion / RFC, and if we want to move in this direction, we can do it incrementally: land this first as a separate API, consider replacing the main |
Should this replace the multiple places in SLPVectorizer.cpp that pass |
Ah, I see - sorry, was only looking at the first version of the function that takes To and From. Thanks for helping me see what's going on. |
@topperc Yes. |
Expose function objects that call into `llvm::isa` and `llvm::isa_and_present`, such that these type checks can be used as predicates in generic algorithms. Before this change, `llvm::isa*` functions cannot be easily used without knowing both the argument type and the checked types, which leads to them being wrapped in lambdas. For example: ```c++ llvm::all_of(myTypes, [](auto type) { return llvm::isa<VectorType>(type); }); ``` With this PR the example above becomes: ```c++ llvm::all_of(myTypes, llvm::IsaPred<VectorType>); ``` As an alternative solution, I considered redefining `isa*` as function objects, but I decided against doing that because it would create assymetry with other cast *functions* and could break code that depends on them being actual functions.
For more context on isa predicates, see: llvm/llvm-project#83753. Also clean up some surrounding casts/isas.
For more context on isa predicates, see: llvm/llvm-project#83753. Also clean up some surrounding casts/isas.
For more context on isa predicates, see: llvm#83753.
For more context on isa predicates, see: llvm#83753.
For more context on isa predicates, see: #83753.
For more context on isa predicates, see: #83753.
Expose function objects that call into `llvm::isa` and `llvm::isa_and_present`, such that these type checks can be used as predicates in generic algorithms. Before this change, `llvm::isa*` functions cannot be easily used without knowing both the argument type and the checked types, which leads to them being wrapped in lambdas. For example: ```c++ llvm::all_of(myTypes, [](auto type) { return llvm::isa<VectorType>(type); }); ``` With this PR the example above becomes: ```c++ llvm::all_of(myTypes, llvm::IsaPred<VectorType>); ``` As an alternative solution, I considered redefining `isa*` as function objects, but I decided against doing that because it would create asymmetry with other cast *functions* and could break code that depends on them being actual functions.
…es (llvm#87183) For more context on isa predicates, see: llvm#83753.
Expose function objects that call into
llvm::isa
andllvm::isa_and_present
, such that these type checks can be used as predicates in generic algorithms.Before this change,
llvm::isa*
functions cannot be easily used without knowing both the argument type and the checked types, which leads to them being wrapped in lambdas. For example:With this PR the example above becomes:
llvm::all_of(myTypes, llvm::IsaPred<VectorType>);
As an alternative solution, I considered redefining
isa*
as function objects, but I decided against doing that because it would create asymmetry with other cast functions and could break code that depends on them being actual functions.