This is an archive of the discontinued LLVM Phabricator instance.

[NFC][mlir] Add support for llvm style casting for mlir types
ClosedPublic

Authored by Tyker on Aug 16 2022, 9:42 PM.

Details

Summary

Note:
when operating on a Type hierarchy with LeafType inheriting from MiddleType which inherits from mlir::Type.
calling LeafType::classof(MiddleType) will always return false.
because classof call the static getTypeID from its parent instead of the dynamic Type::getTypeID
so classof in this context will check if the TypeID of LeafType is the same as the TypeID of MiddleType which is always false.
It is bypassed in this commit inside CastInfo<To, From>::isPossible by calling classof with an mlir::Type.
but other unsuspecting users of LeafType::classof(MiddleType) would still get an incorrect result.

Diff Detail

Event Timeline

Tyker created this revision.Aug 16 2022, 9:42 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 16 2022, 9:42 PM
Tyker requested review of this revision.Aug 16 2022, 9:42 PM
mehdi_amini accepted this revision.Aug 17 2022, 4:21 AM

Sweet!

mlir/include/mlir/IR/Types.h
324

Can you add a one-line comment here?

This revision is now accepted and ready to land.Aug 17 2022, 4:21 AM

+1 to the concept, many thanks for this! I think it makes more sense to implement the member versions with the llvm-style versions, it should simplify the member style code because then you won't have to manually implement the classof/isa/cast innards it'd just be cast<T>(*this) or something.

mlir/unittests/IR/TypeTest.cpp
29

_or_null is deprecated, please use _if_present

Tyker updated this revision to Diff 453516.EditedAug 17 2022, 8:23 PM

+1 to the concept, many thanks for this! I think it makes more sense to implement the member versions with the llvm-style versions, it should simplify the member style code because then you won't have to manually implement the classof/isa/cast innards it'd just be cast<T>(*this) or something.

I think both casting system will stay available. so I am not sure it matters.
i found out that some Types can "inherit" from other type so it makes sense to cast from a more then just mlir::Type.
so I added support for that. it may be good to rereview

mlir/unittests/IR/TypeTest.cpp
29

it isn't appear to be attributed as such.

Tyker marked 2 inline comments as done.Aug 17 2022, 8:24 PM
bzcheeseman added inline comments.Aug 17 2022, 8:29 PM
mlir/unittests/IR/TypeTest.cpp
29

We didn't want to add a bunch of build spew for all LLVM users immediately, it will be marked deprecated at some point (hopefully) soon.

I think both casting system will stay available. so I am not sure it matters.

Actually we should likely deprecated the old-style in the future, so the suggestion above gives a nicer path to get there.

I think both casting system will stay available. so I am not sure it matters.

Actually we should likely deprecated the old-style in the future, so the suggestion above gives a nicer path to get there.

+1, we intend to get rid of the old method.

Tyker added a comment.EditedAug 18 2022, 6:17 PM

I updated to to have the member version of cast/isa/dyn_cast depend on the out of line ones.

and I found an interesting bug. when having a type hierarchy like the following

struct LeafType;

struct MiddleType : Type::TypeBase<MiddleType, Type, TypeStorage> {
  using Base::Base;
  static bool classof(Type ty) {
    return ty.getTypeID() == TypeID::get<LeafType>() || Base::classof(ty);
  }
};

struct LeafType : Type::TypeBase<LeafType, MiddleType, TypeStorage> {
  using Base::Base;
};

calling LeafType::classof(MiddleType) will always return false.

here is the current implementation of StorageUserBase::classof

template <typename T>
static bool classof(T val) {
  static_assert(std::is_convertible<ConcreteT, T>::value,
                "casting from a non-convertible type");
  return val.getTypeID() == getTypeID();
}

When T is not mlir::Type, val.getTypeID() will call the static getTypeID from its parent instead of the dynamic Type::getTypeID
so classof in the context will check if the TypeID of LeafType is the same as the TypeID of MiddleType which is always false.

It is "solved" in the current version of the patch inside CastInfo<To, From>::isPossible by calling classof with an mlir::Type.
but other unsuspecting users of LeafType::classof(MiddleType) would still get an incorrect result.

is there any benefit to having Type::getTypeID and StorageUserBase::getTypeID have the same name ?
should the StorageUserBase::getTypeID change name to avoid this issue?
or make classof Take a mlir::Type.

also is StorageUserBase used for attributes ?

Tyker updated this revision to Diff 453851.Aug 18 2022, 6:17 PM
bzcheeseman added inline comments.Aug 19 2022, 7:58 AM
mlir/include/mlir/IR/Types.h
328

Not sure if you saw https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html#advanced-use-cases but you might be able to use some cast traits since Type is nullable. I think you can use CastIsPossible, NullableValueCastFailed, and DefaultDoCastIfPossible. The default CastIsPossible also checks if the types are the same or related by inheritance and always allows casting from derived to base.

For context, that example nullable type was more or less based on mlir::Type.

Tyker updated this revision to Diff 454345.Aug 21 2022, 5:43 PM
Tyker marked 2 inline comments as done.
Tyker added inline comments.
mlir/include/mlir/IR/Types.h
328

thanks for the tip, I added NullableValueCastFailed and DefaultDoCastIfPossible

bzcheeseman accepted this revision.EditedAug 21 2022, 5:47 PM

Thanks for fixing the or_null to if_present too (where it makes sense). LGTM as long as everything passes CI :)

mlir/include/mlir/IR/Types.h
341

I take it this means you can't use the CastIsPossible<To, From> trait?

Tyker added inline comments.Aug 21 2022, 6:01 PM
mlir/include/mlir/IR/Types.h
341

I couldn't get it to work easily. I think i could have by insisting a bit more but it was in my opinion more trouble then it was worth.

CastIsPossible by default passes pointers to classof. so it doesn't work for us.
and adding our own specialization would mean we are ambiguous with the CastIsPossible that detect the up cast.

we could always add an and not up-cast in our SFINAE but this is in my opinion more trouble that it is worth.

Tyker added a comment.Aug 21 2022, 6:08 PM

I would still like a comment for what to do about this issue (which i bypassed):

and I found an interesting bug. when having a type hierarchy like the following

struct LeafType;

struct MiddleType : Type::TypeBase<MiddleType, Type, TypeStorage> {
  using Base::Base;
  static bool classof(Type ty) {
    return ty.getTypeID() == TypeID::get<LeafType>() || Base::classof(ty);
  }
};

struct LeafType : Type::TypeBase<LeafType, MiddleType, TypeStorage> {
  using Base::Base;
};

calling LeafType::classof(MiddleType) will always return false.

here is the current implementation of StorageUserBase::classof

template <typename T>
static bool classof(T val) {
  static_assert(std::is_convertible<ConcreteT, T>::value,
                "casting from a non-convertible type");
  return val.getTypeID() == getTypeID();
}

When T is not mlir::Type, val.getTypeID() will call the static getTypeID from its parent instead of the dynamic Type::getTypeID
so classof in the context will check if the TypeID of LeafType is the same as the TypeID of MiddleType which is always false.

It is "solved" in the current version of the patch inside CastInfo<To, From>::isPossible by calling classof with an mlir::Type.
but other unsuspecting users of LeafType::classof(MiddleType) would still get an incorrect result.

is there any benefit to having Type::getTypeID and StorageUserBase::getTypeID have the same name ?
should the StorageUserBase::getTypeID change name to avoid this issue?
or make classof Take a mlir::Type.

also is StorageUserBase used for attributes ?

Can you please add a commit summary along the lines captured in the comments?

Tyker edited the summary of this revision. (Show Details)Aug 24 2022, 9:11 AM