Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ def AssumeAlignmentOp : MemRef_Op<"assume_alignment", [

def DistinctObjectsOp : MemRef_Op<"distinct_objects", [
Pure,
DistinctObjectsTrait,
DeclareOpInterfaceMethods<InferTypeOpInterface>
// ViewLikeOpInterface TODO: ViewLikeOpInterface only supports a single argument
]> {
Expand Down
16 changes: 16 additions & 0 deletions mlir/include/mlir/Interfaces/ViewLikeInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,22 @@ LogicalResult verifyListOfOperandsOrIntegers(Operation *op, StringRef name,
ArrayRef<int64_t> attr,
ValueRange values);

namespace OpTrait {
/// This trai indicates that pointer-like objects (such as memrefs) returned
/// from this operation will never alias with each other. This provides a
/// guarantee to optimization passes that accesses through different results
/// of this operation can be safely reordered, as they will never reference
/// overlapping memory locations.
///
/// Operations with this trait take multiple pointer-like operands
/// and return the same operands with additional non-aliasing guarantees.
/// If the access to the results of this operation aliases at runtime, the
/// behavior of such access is undefined.
template <typename ConcreteType>
class DistinctObjectsTrait
: public TraitBase<ConcreteType, DistinctObjectsTrait> {};
} // namespace OpTrait

} // namespace mlir

#endif // MLIR_INTERFACES_VIEWLIKEINTERFACE_H_
12 changes: 12 additions & 0 deletions mlir/include/mlir/Interfaces/ViewLikeInterface.td
Original file line number Diff line number Diff line change
Expand Up @@ -414,4 +414,16 @@ def OffsetSizeAndStrideOpInterface : OpInterface<"OffsetSizeAndStrideOpInterface
}];
}

// This trai indicates that pointer-like objects (such as memrefs) returned
// from this operation will never alias with each other. This provides a
// guarantee to optimization passes that accesses through different results
// of this operation can be safely reordered, as they will never reference
// overlapping memory locations.
//
// Operations with this trait take multiple pointer-like operands
// and return the same operands with additional non-aliasing guarantees.
// If the access to the results of this operation aliases at runtime, the
// behavior of such access is undefined.
def DistinctObjectsTrait : NativeOpTrait<"DistinctObjectsTrait">;

#endif // MLIR_INTERFACES_VIEWLIKEINTERFACE
36 changes: 36 additions & 0 deletions mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,39 @@ getAllocEffectFor(Value value,
return success();
}

static Operation *isDistinctObjectsOp(Operation *op) {
if (op && op->hasTrait<OpTrait::DistinctObjectsTrait>())
return op;

return nullptr;
}

static Value getDistinctObjectsOperand(Operation *op, Value value) {
unsigned argNumber = cast<OpResult>(value).getResultNumber();
return op->getOperand(argNumber);
}

static std::optional<AliasResult> checkDistinctObjects(Value lhs, Value rhs) {
// We should already checked that lhs and rhs are different.
assert(lhs != rhs && "lhs and rhs must be different");

// Result and corresponding operand must alias.
auto lhsOp = isDistinctObjectsOp(lhs.getDefiningOp());
if (lhsOp && getDistinctObjectsOperand(lhsOp, lhs) == rhs)
return AliasResult::MustAlias;

auto rhsOp = isDistinctObjectsOp(rhs.getDefiningOp());
if (rhsOp && getDistinctObjectsOperand(rhsOp, rhs) == lhs)
return AliasResult::MustAlias;

// If two different values come from the same `DistinctObjects` operation,
// they don't alias.
if (lhsOp && lhsOp == rhsOp)
return AliasResult::NoAlias;

return std::nullopt;
}

/// Given the two values, return their aliasing behavior.
AliasResult LocalAliasAnalysis::aliasImpl(Value lhs, Value rhs) {
if (lhs == rhs)
Expand Down Expand Up @@ -289,6 +322,9 @@ AliasResult LocalAliasAnalysis::aliasImpl(Value lhs, Value rhs) {
: AliasResult::MayAlias;
}

if (std::optional<AliasResult> result = checkDistinctObjects(lhs, rhs))
return *result;

// Otherwise, neither of the values are constant so check to see if either has
// an allocation effect.
bool lhsHasAlloc = succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope));
Expand Down
16 changes: 16 additions & 0 deletions mlir/test/Analysis/test-alias-analysis.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,19 @@ func.func @constants(%arg: memref<2xf32>) attributes {test.ptr = "func"} {

return
}

// -----

// CHECK-LABEL: Testing : "distinct_objects"
// CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias

// CHECK-DAG: distinct#0 <-> distinct#1: NoAlias
// CHECK-DAG: distinct#0 <-> func.region0#0: MustAlias
// CHECK-DAG: distinct#1 <-> func.region0#0: MayAlias
// CHECK-DAG: distinct#0 <-> func.region0#1: MayAlias
// CHECK-DAG: distinct#1 <-> func.region0#1: MustAlias

func.func @distinct_objects(%arg: memref<?xf32>, %arg1: memref<?xf32>) attributes {test.ptr = "func"} {
%0, %1 = memref.distinct_objects %arg, %arg1 {test.ptr = "distinct"} : memref<?xf32>, memref<?xf32>
return
}