I've been fiddling with the modeling of the numpy ufunc abstractions, because if you get that right, you have a large swath of standard operations.
Mapped to MLIR, they represent several ops: a definition (which defines a module level symbol) and various operations on the ufunc (such as ufunc_call, ufunc_reduce, ufunc_accumulate, ufunc_reduceat, ufunc_outer, ufunc_at). (The at variants perform in place updates)
The ufunc definition itself defines some metadata but is primarily an overloaded set of FunctionType signatures that operate on scalars. I've modeled this as an op that combines an array of FunctionType and variadic of regions:
def Numpy_GenericUfuncOp : Numpy_Op<"generic_ufunc", [
IsolatedFromAbove,