-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[RFC][mlir] ViewLikeOpInterface method for detecting partial views. #164020
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
base: main
Are you sure you want to change the base?
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 |
|---|---|---|
|
|
@@ -212,6 +212,10 @@ AliasResult AliasAnalysis::alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs, | |
| if (lhsSrc.origin == rhsSrc.origin) { | ||
| LLVM_DEBUG(llvm::dbgs() | ||
| << " aliasing because same source kind and origin\n"); | ||
| // TODO: we should return PartialAlias here. Need to decide | ||
| // if returning PartialAlias for fir.pack_array is okay; | ||
| // if not, then we need to handle it specially to still return | ||
| // MayAlias. | ||
| if (approximateSource) | ||
| return AliasResult::MayAlias; | ||
| return AliasResult::MustAlias; | ||
|
|
@@ -559,10 +563,19 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, | |
| type = SourceKind::Allocate; | ||
| breakFromLoop = true; | ||
| }) | ||
| .Case<fir::ConvertOp>([&](auto op) { | ||
| // Skip ConvertOp's and track further through the operand. | ||
| v = op->getOperand(0); | ||
| .Case<mlir::ViewLikeOpInterface>([&](auto op) { | ||
|
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. Why does Flang have its own alias analysis? How is it different from the MLIR alias analysis? Given that you are improving this interface, could the Flang alias analysis be replaced with the one from MLIR? 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. Flang alias analysis has special handling for HLFIR/FIR operations that carry extra information about the rules of aliasing for objects created from Fortran variables. For example, there are special rules for DUMMY arguments of functions that cannot alias each other unless they have TARGET or POINTER attributes. Flang carries this extra information on I think the HLFIR/FIR specifics used by Flang alias analysis can be abstracted by using some generic interfaces that |
||
| if (isPointerReference(ty)) | ||
| attributes.set(Attribute::Pointer); | ||
| v = op.getViewSource(); | ||
| defOp = v.getDefiningOp(); | ||
| // If the source is a box, and the result is not a box, | ||
| // then this is one of the box "unpacking" operations, | ||
| // so we should set followingData. | ||
| if (mlir::isa<fir::BaseBoxType>(v.getType()) && | ||
| !mlir::isa<fir::BaseBoxType>(ty)) | ||
| followBoxData = true; | ||
| if (!op.isKnownToHaveSameStart()) | ||
| approximateSource = true; | ||
| }) | ||
| .Case<fir::PackArrayOp>([&](auto op) { | ||
| // The packed array is not distinguishable from the original | ||
|
|
@@ -578,15 +591,6 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, | |
| if (mlir::isa<fir::BaseBoxType>(v.getType())) | ||
| followBoxData = true; | ||
| }) | ||
| .Case<fir::ArrayCoorOp, fir::CoordinateOp>([&](auto op) { | ||
| if (isPointerReference(ty)) | ||
| attributes.set(Attribute::Pointer); | ||
| v = op->getOperand(0); | ||
| defOp = v.getDefiningOp(); | ||
| if (mlir::isa<fir::BaseBoxType>(v.getType())) | ||
| followBoxData = true; | ||
| approximateSource = true; | ||
| }) | ||
| .Case<fir::EmboxOp, fir::ReboxOp>([&](auto op) { | ||
| if (followBoxData) { | ||
| v = op->getOperand(0); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,9 +35,12 @@ class AliasResult { | |
| /// The two locations may or may not alias. This is the least precise | ||
| /// result. | ||
| MayAlias, | ||
| /// The two locations alias, but only due to a partial overlap. | ||
| /// The two locations overlap in some way, regardless of whether | ||
| /// they start at the same address or not. | ||
|
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. This seems to change the semantics 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. I think the new comment is a clarification of the old one ( Yes, I decided to reuse the LLVM definition here, which seems more verbose to me. I believe there are no current check for |
||
| PartialAlias, | ||
| /// The two locations precisely alias each other. | ||
| /// The two locations precisely alias each other, meaning that | ||
| /// they always start at exactly the same location. | ||
| /// This result does not imply that the pointers compare equal. | ||
| MustAlias, | ||
|
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. I think it's worth mentioning as a comment of the enum that these values are taken from LLVM. 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. Thanks! I will add a comment. |
||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,21 +23,53 @@ def ViewLikeOpInterface : OpInterface<"ViewLikeOpInterface"> { | |
| }]; | ||
| let cppNamespace = "::mlir"; | ||
|
|
||
| let methods = [ | ||
| InterfaceMethod< | ||
| "Returns the source buffer from which the view is created.", | ||
| "::mlir::Value", "getViewSource">, | ||
| InterfaceMethod< | ||
| /*desc=*/[{ Returns the buffer which the view created. }], | ||
| /*retTy=*/"::mlir::Value", | ||
| /*methodName=*/"getViewDest", | ||
| /*args=*/(ins), | ||
| /*methodBody=*/"", | ||
| /*defaultImplementation=*/[{ | ||
| let methods = | ||
| [InterfaceMethod< | ||
| "Returns the source buffer from which the view is created.", | ||
| "::mlir::Value", "getViewSource">, | ||
| InterfaceMethod< | ||
| /*desc=*/[{ Returns the buffer which the view created. }], | ||
| /*retTy=*/"::mlir::Value", | ||
| /*methodName=*/"getViewDest", | ||
| /*args=*/(ins), | ||
| /*methodBody=*/"", | ||
| /*defaultImplementation=*/[{ | ||
| return $_op->getResult(0); | ||
| }] | ||
| > | ||
| ]; | ||
| }]>, | ||
| InterfaceMethod< | ||
| /*desc=*/ | ||
| [{ | ||
| Returns true iff the source buffer and the resulting view | ||
| are known to start at the same "address". | ||
| }], | ||
| /*retTy=*/"bool", | ||
| /*methodName=*/"isKnownToHaveSameStart", | ||
|
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 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. Yes, I think adding a method like At the same time, I think the new I think the following pieces of "basic" information provide everything we need to make a decision about the aliasing of the input and resulting views:
I believe having these "basic" methods available, we can implement With that said, I agree replacing the two methods with an I can think of the following specification for the new
Does this definition make sense to you? 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. I just noticed that
I don't follow yet. What's the benefit of having "basic" methods if everything is expressible by a single method that returns
Is that actually correct according to the LLVM definition of these enum values? Even if both memrefs have size 0, their pointers could be the same. This sounds like At the same time, LLVM's definition talks about:
This is not necessarily the case. |
||
| /*args=*/(ins), | ||
| /*methodBody=*/"", | ||
| /*defaultImplementation=*/[{ | ||
| return false; | ||
|
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. Is it safe for this to have a default implementation? Same question for 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. I added the default implementations so that they are conservatively correct, assuming that the methods will be used to perform some transformations based on the fact that a view starts at the same address as the source or/and a view is a complete view of the source. Maybe, the names of the methods are confusing. Would it be more clear if I named them |
||
| }]>, | ||
| InterfaceMethod< | ||
| /*desc=*/ | ||
| [{ | ||
| Returns true iff the resulting view is known to be | ||
| a complete view of the source buffer, i.e. | ||
| isKnownToHaveSameStart() is true and the result | ||
| has the same total size and is not a subview of the input. | ||
| }], | ||
| /*retTy=*/"bool", | ||
| /*methodName=*/"isKnownToBeCompleteView", | ||
| /*args=*/(ins), | ||
| /*methodBody=*/"", | ||
| /*defaultImplementation=*/[{ | ||
| return false; | ||
| }]>]; | ||
|
|
||
| let verify = [{ | ||
| auto concreteOp = ::mlir::cast<ConcreteOp>($_op); | ||
| // isKnownToBeCompleteView() == true implies isKnownToHaveSameStart() == true. | ||
| return !concreteOp.isKnownToBeCompleteView() || concreteOp.isKnownToHaveSameStart() ? ::mlir::success() : ::mlir::failure(); | ||
| }]; | ||
| } | ||
|
|
||
| def OffsetSizeAndStrideOpInterface : OpInterface<"OffsetSizeAndStrideOpInterface"> { | ||
|
|
||
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.
From the LLVM documentation:
I guess it depends what exactly is meant by "object". Previously I had interpreted it as referring to the specific memory addresses which could be accessed. So
array(1:4)andarray(3)do overlap with each other but not witharray(100:200). Therefore I think array indexing (until analysed more carefully) should useMayAliasrather thanPartialAlias.Alternatively, if "object" means the entire allocation then
PartialAliasmakes sense here.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.
Thanks for looking, Tom!
I believe in LLVM alias analysis an "object" is defined at a high level as a starting address and the size that are passed to the
aliasAPI. So, indeed, the accessesarray(1:4)andarray(3)do partially overlap, and they do not alias witharray(100:200). Now, from the implementation point of view, analiasquery may be made with an "infinite" size (up to the end of the underlying memory object). In this case,array(1:4),array(3)andarray(100:200)all partially overlap.When the starting addresses of the two accesses are not compilation constants and cannot be proven to be the same/different, then they may be in both
MustAliasandPartialAliasstates, so returningMayAliasseems reasonable.The current implementation of MLIR alias analysis does not accept the access size and I assume that it is "infinite", so returning
PartialAliasfor such accesses seems conservatively correct to me.At the same time, maybe I should drop my changes to return
PartialAliasinLocalAliasAnalysisand always returnMayAliasto allow further alias analyses to make up a more precise answer.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.
Thinking about it some more, I think local alias analysis should at least return MayAlias. As I understand it, the distinction between
MayAliasandPartialAliasis that aMayAliasresult invites further analysis by other methods whereasPartialAliasis considered a definate "these accesses are proven to overlap" and so does not need further analysis if other alias analysis implementations are available.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.
I agree with you, Tom. I think the current algorithm in LocalAliasAnalysis (which I am not trying to affect too much, because this is not my goal) does not allow reasoning about partial aliases quite precisely. So I feel okay returning
MayAliasin cases where there might be an offset involved. At least, this makes the analysis conservatively correct.