-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[flang] Restructured TBAA trees in AddAliasTags pass. #136725
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
Conversation
This patch produces the following TBAA tree for a function: Function root | "any access" | |- "descriptor member" |- "any data access" | |- "dummy arg data" |- "target data" | |- "allocated data" |- "direct data" |- "global data" The TBAA tags are assigned using the following logic: * All POINTER variables point to the root of "target data". * Dummy arguments without POINTER/TARGET point to their leafs under "dummy arg data". * Dummy arguments with TARGET point to the root of "target data". * Global variables without descriptors point to their leafs under "global data" (including the ones with TARGET). * Global variables with descriptors point to their leafs under "direct data" (including the ones with TARGET). * Locally allocated variables point to their leafs under "allocated data" (including the ones with TARGET). This change makes it possible to disambiguate globals like: module data real, allocatable :: a(:) real, allocatable, target :: b(:) end Indeed, two direct references to global vars cannot alias even if any/both of them have TARGET attribute. In addition, the dummy arguments without POINTER/TARGET cannot alias any other variable even with POINTER/TARGET. This was not expressed in TBAA before this change. As before, any "unknown" memory references (such as with Indirect source, as classified by FIR alias analysis) may alias with anything, as long as they point to the root of "any access". Please think of the counterexamples for which this structure may not work.
@llvm/pr-subscribers-flang-fir-hlfir Author: Slava Zakharin (vzakhari) ChangesThis patch produces the following TBAA tree for a function:
The TBAA tags are assigned using the following logic:
This change makes it possible to disambiguate globals like:
Indeed, two direct references to global vars cannot alias As before, any "unknown" memory references (such as with Indirect Please think of the counterexamples for which this structure Patch is 59.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/136725.diff 10 Files Affected:
diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index c71988d081dd0..30dd5f754d0b5 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -155,6 +155,12 @@ struct AliasAnalysis {
/// Return true, if Target or Pointer attribute is set.
bool isTargetOrPointer() const;
+ /// Return true, if Target attribute is set.
+ bool isTarget() const;
+
+ /// Return true, if Pointer attribute is set.
+ bool isPointer() const;
+
bool isDummyArgument() const;
bool isData() const;
bool isBoxData() const;
diff --git a/flang/include/flang/Optimizer/Analysis/TBAAForest.h b/flang/include/flang/Optimizer/Analysis/TBAAForest.h
index f95fcffd96e93..4d2281642b43d 100644
--- a/flang/include/flang/Optimizer/Analysis/TBAAForest.h
+++ b/flang/include/flang/Optimizer/Analysis/TBAAForest.h
@@ -40,6 +40,12 @@ struct TBAATree {
mlir::LLVM::TBAATagAttr getTag(llvm::StringRef uniqueId) const;
+ /// Create a TBAA tag pointing to the root of this subtree,
+ /// i.e. all the children tags will alias with this tag.
+ mlir::LLVM::TBAATagAttr getTag() const;
+
+ mlir::LLVM::TBAATypeDescriptorAttr getRoot() const { return parent; }
+
private:
SubtreeState(mlir::MLIRContext *ctx, std::string name,
mlir::LLVM::TBAANodeAttr grandParent)
@@ -51,17 +57,44 @@ struct TBAATree {
const std::string parentId;
mlir::MLIRContext *const context;
mlir::LLVM::TBAATypeDescriptorAttr parent;
- llvm::DenseMap<llvm::StringRef, mlir::LLVM::TBAATagAttr> tagDedup;
};
+ /// A subtree for POINTER/TARGET variables data.
+ /// Any POINTER variable must use a tag that points
+ /// to the root of this subtree.
+ /// A TARGET dummy argument must also point to this root.
+ SubtreeState targetDataTree;
+ /// A subtree for global variables data (e.g. user module variables).
SubtreeState globalDataTree;
+ /// A subtree for variables allocated via fir.alloca or fir.allocmem.
SubtreeState allocatedDataTree;
+ /// A subtree for subprogram's dummy arguments.
+ /// It only contains children for the dummy arguments
+ /// that are not POINTER/TARGET. They all do not conflict
+ /// with each other and with any other data access, except
+ /// with unknown data accesses (FIR alias analysis uses
+ /// SourceKind::Indirect for sources of such accesses).
SubtreeState dummyArgDataTree;
+ /// A subtree for global variables descriptors.
SubtreeState directDataTree;
mlir::LLVM::TBAATypeDescriptorAttr anyAccessDesc;
mlir::LLVM::TBAATypeDescriptorAttr boxMemberTypeDesc;
mlir::LLVM::TBAATypeDescriptorAttr anyDataTypeDesc;
+ // Structure of the created tree:
+ // Function root
+ // |
+ // "any access"
+ // |
+ // |- "descriptor member"
+ // |- "any data access"
+ // |
+ // |- "dummy arg data"
+ // |- "target data"
+ // |
+ // |- "allocated data"
+ // |- "direct data"
+ // |- "global data"
static TBAATree buildTree(mlir::StringAttr functionName);
private:
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index bda98871a1c7a..cbfc8b63ab64d 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -110,6 +110,14 @@ bool AliasAnalysis::Source::isTargetOrPointer() const {
attributes.test(Attribute::Target);
}
+bool AliasAnalysis::Source::isTarget() const {
+ return attributes.test(Attribute::Target);
+}
+
+bool AliasAnalysis::Source::isPointer() const {
+ return attributes.test(Attribute::Pointer);
+}
+
bool AliasAnalysis::Source::isDummyArgument() const {
if (auto v = origin.u.dyn_cast<mlir::Value>()) {
return fir::isDummyArgument(v);
diff --git a/flang/lib/Optimizer/Analysis/TBAAForest.cpp b/flang/lib/Optimizer/Analysis/TBAAForest.cpp
index 786c4932ea89e..cce50e0de1bc7 100644
--- a/flang/lib/Optimizer/Analysis/TBAAForest.cpp
+++ b/flang/lib/Optimizer/Analysis/TBAAForest.cpp
@@ -11,9 +11,6 @@
mlir::LLVM::TBAATagAttr
fir::TBAATree::SubtreeState::getTag(llvm::StringRef uniqueName) const {
- // mlir::LLVM::TBAATagAttr &tag = tagDedup[uniqueName];
- // if (tag)
- // return tag;
std::string id = (parentId + "/" + uniqueName).str();
mlir::LLVM::TBAATypeDescriptorAttr type =
mlir::LLVM::TBAATypeDescriptorAttr::get(
@@ -22,6 +19,10 @@ fir::TBAATree::SubtreeState::getTag(llvm::StringRef uniqueName) const {
// return tag;
}
+mlir::LLVM::TBAATagAttr fir::TBAATree::SubtreeState::getTag() const {
+ return mlir::LLVM::TBAATagAttr::get(parent, parent, 0);
+}
+
fir::TBAATree fir::TBAATree::buildTree(mlir::StringAttr func) {
llvm::StringRef funcName = func.getValue();
std::string rootId = ("Flang function root " + funcName).str();
@@ -53,9 +54,13 @@ fir::TBAATree fir::TBAATree::buildTree(mlir::StringAttr func) {
fir::TBAATree::TBAATree(mlir::LLVM::TBAATypeDescriptorAttr anyAccess,
mlir::LLVM::TBAATypeDescriptorAttr dataRoot,
mlir::LLVM::TBAATypeDescriptorAttr boxMemberTypeDesc)
- : globalDataTree(dataRoot.getContext(), "global data", dataRoot),
- allocatedDataTree(dataRoot.getContext(), "allocated data", dataRoot),
+ : targetDataTree(dataRoot.getContext(), "target data", dataRoot),
+ globalDataTree(dataRoot.getContext(), "global data",
+ targetDataTree.getRoot()),
+ allocatedDataTree(dataRoot.getContext(), "allocated data",
+ targetDataTree.getRoot()),
dummyArgDataTree(dataRoot.getContext(), "dummy arg data", dataRoot),
- directDataTree(dataRoot.getContext(), "direct data", dataRoot),
+ directDataTree(dataRoot.getContext(), "direct data",
+ targetDataTree.getRoot()),
anyAccessDesc(anyAccess), boxMemberTypeDesc(boxMemberTypeDesc),
anyDataTypeDesc(dataRoot) {}
diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
index e6fc2ed992e38..66b4b84998801 100644
--- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
+++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
@@ -198,12 +198,6 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
LLVM_DEBUG(llvm::dbgs() << "Analysing " << op << "\n");
const fir::AliasAnalysis::Source &source = state.getSource(memref);
- if (source.isTargetOrPointer()) {
- LLVM_DEBUG(llvm::dbgs().indent(2) << "Skipping TARGET/POINTER\n");
- // These will get an "any data access" tag in TBAABuilder (CodeGen): causing
- // them to "MayAlias" with all non-box accesses
- return;
- }
// Process the scopes, if not processed yet.
state.processFunctionScopes(func);
@@ -228,15 +222,21 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "Found reference to dummy argument at " << *op << "\n");
std::string name = getFuncArgName(llvm::cast<mlir::Value>(source.origin.u));
- if (!name.empty())
+ // If it is a TARGET or POINTER, then we do not care about the name,
+ // because the tag points to the root of the subtree currently.
+ if (source.isTargetOrPointer()) {
+ tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
+ } else if (!name.empty()) {
tag = state.getFuncTreeWithScope(func, scopeOp)
.dummyArgDataTree.getTag(name);
- else
+ } else {
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "WARN: couldn't find a name for dummy argument " << *op
<< "\n");
+ tag = state.getFuncTreeWithScope(func, scopeOp).dummyArgDataTree.getTag();
+ }
- // TBAA for global variables
+ // TBAA for global variables without descriptors
} else if (enableGlobals &&
source.kind == fir::AliasAnalysis::SourceKind::Global &&
!source.isBoxData()) {
@@ -244,9 +244,13 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
const char *name = glbl.getRootReference().data();
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global " << name
<< " at " << *op << "\n");
- tag = state.getFuncTreeWithScope(func, scopeOp).globalDataTree.getTag(name);
+ if (source.isPointer())
+ tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
+ else
+ tag =
+ state.getFuncTreeWithScope(func, scopeOp).globalDataTree.getTag(name);
- // TBAA for SourceKind::Direct
+ // TBAA for global variables with descriptors
} else if (enableDirect &&
source.kind == fir::AliasAnalysis::SourceKind::Global &&
source.isBoxData()) {
@@ -254,11 +258,12 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
const char *name = glbl.getRootReference().data();
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to direct " << name
<< " at " << *op << "\n");
- tag =
- state.getFuncTreeWithScope(func, scopeOp).directDataTree.getTag(name);
+ if (source.isPointer())
+ tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
+ else
+ tag = state.getFuncTreeWithScope(func, scopeOp)
+ .directDataTree.getTag(name);
} else {
- // SourceKind::Direct is likely to be extended to cases which are not a
- // SymbolRefAttr in the future
LLVM_DEBUG(llvm::dbgs().indent(2) << "Can't get name for direct "
<< source << " at " << *op << "\n");
}
@@ -269,11 +274,23 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
std::optional<llvm::StringRef> name;
mlir::Operation *sourceOp =
llvm::cast<mlir::Value>(source.origin.u).getDefiningOp();
+ bool unknownAllocOp = false;
if (auto alloc = mlir::dyn_cast_or_null<fir::AllocaOp>(sourceOp))
name = alloc.getUniqName();
else if (auto alloc = mlir::dyn_cast_or_null<fir::AllocMemOp>(sourceOp))
name = alloc.getUniqName();
- if (name) {
+ else
+ unknownAllocOp = true;
+
+ if (unknownAllocOp) {
+ LLVM_DEBUG(llvm::dbgs().indent(2)
+ << "WARN: unknown defining op for SourceKind::Allocate " << *op
+ << "\n");
+ } else if (source.isPointer()) {
+ LLVM_DEBUG(llvm::dbgs().indent(2)
+ << "Found reference to allocation at " << *op << "\n");
+ tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
+ } else if (name) {
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to allocation "
<< name << " at " << *op << "\n");
tag = state.getFuncTreeWithScope(func, scopeOp)
@@ -282,6 +299,8 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "WARN: couldn't find a name for allocation " << *op
<< "\n");
+ tag =
+ state.getFuncTreeWithScope(func, scopeOp).allocatedDataTree.getTag();
}
} else {
if (source.kind != fir::AliasAnalysis::SourceKind::Argument &&
diff --git a/flang/test/Transforms/tbaa-with-dummy-scope.fir b/flang/test/Transforms/tbaa-with-dummy-scope.fir
index 738692814cde5..7624de9431e08 100644
--- a/flang/test/Transforms/tbaa-with-dummy-scope.fir
+++ b/flang/test/Transforms/tbaa-with-dummy-scope.fir
@@ -2,7 +2,7 @@
// subroutine test(x, y)
// real, target :: x, y
-// x = y ! the load/store do not have TBAA due to TARGET
+// x = y
// call inner(x, y) ! the inlined load/store go to Scope 1
// call inner(x, y) ! the inlined load/store go to Scope 2
// contains
@@ -12,25 +12,30 @@
// end subroutine inner
// end subroutine test
+// CHECK: #[[TEST1ROOT:.+]] = #llvm.tbaa_root<id = "Flang function root test1">
// CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root<id = "Flang function root test1 - Scope 1">
// CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_root<id = "Flang function root test1 - Scope 2">
-// CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc<id = "any access", members = {<#tbaa_root, 0>}>
-// CHECK: #[[$ATTR_3:.+]] = #llvm.tbaa_type_desc<id = "any access", members = {<#tbaa_root1, 0>}>
-// CHECK: #[[$ATTR_4:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#tbaa_type_desc, 0>}>
-// CHECK: #[[$ATTR_5:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#tbaa_type_desc1, 0>}>
-// CHECK: #[[$ATTR_6:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data", members = {<#tbaa_type_desc2, 0>}>
-// CHECK: #[[$ATTR_7:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data", members = {<#tbaa_type_desc3, 0>}>
-// CHECK: #[[$ATTR_8:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEy", members = {<#tbaa_type_desc4, 0>}>
-// CHECK: #[[$ATTR_9:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEx", members = {<#tbaa_type_desc4, 0>}>
-// CHECK: #[[$ATTR_10:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEy", members = {<#tbaa_type_desc5, 0>}>
-// CHECK: #[[$ATTR_11:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEx", members = {<#tbaa_type_desc5, 0>}>
-// CHECK: #[[$ATTR_12:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc6, access_type = #tbaa_type_desc6, offset = 0>
-// CHECK: #[[$ATTR_13:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc7, access_type = #tbaa_type_desc7, offset = 0>
-// CHECK: #[[$ATTR_14:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc8, access_type = #tbaa_type_desc8, offset = 0>
-// CHECK: #[[$ATTR_15:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc9, access_type = #tbaa_type_desc9, offset = 0>
+// CHECK: #[[TEST1ANYACCESS:.+]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[TEST1ROOT]], 0>}>
+// CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[$ATTR_0]], 0>}>
+// CHECK: #[[$ATTR_3:.+]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[$ATTR_1]], 0>}>
+// CHECK: #[[TEST1ANYDATA:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[TEST1ANYACCESS]], 0>}>
+// CHECK: #[[$ATTR_4:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[$ATTR_2]], 0>}>
+// CHECK: #[[$ATTR_5:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[$ATTR_3]], 0>}>
+// CHECK: #[[TARGETDATA:.+]] = #llvm.tbaa_type_desc<id = "target data", members = {<#[[TEST1ANYDATA]], 0>}>
+// CHECK: #[[$ATTR_6:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data", members = {<#[[$ATTR_4]], 0>}>
+// CHECK: #[[$ATTR_7:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data", members = {<#[[$ATTR_5]], 0>}>
+// CHECK: #[[TARGETTAG:.+]] = #llvm.tbaa_tag<base_type = #[[TARGETDATA]], access_type = #[[TARGETDATA]], offset = 0>
+// CHECK: #[[$ATTR_8:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEy", members = {<#[[$ATTR_6]], 0>}>
+// CHECK: #[[$ATTR_9:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEx", members = {<#[[$ATTR_6]], 0>}>
+// CHECK: #[[$ATTR_10:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEy", members = {<#[[$ATTR_7]], 0>}>
+// CHECK: #[[$ATTR_11:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestFinnerEx", members = {<#[[$ATTR_7]], 0>}>
+// CHECK: #[[$ATTR_12:.+]] = #llvm.tbaa_tag<base_type = #[[$ATTR_8]], access_type = #[[$ATTR_8]], offset = 0>
+// CHECK: #[[$ATTR_13:.+]] = #llvm.tbaa_tag<base_type = #[[$ATTR_9]], access_type = #[[$ATTR_9]], offset = 0>
+// CHECK: #[[$ATTR_14:.+]] = #llvm.tbaa_tag<base_type = #[[$ATTR_10]], access_type = #[[$ATTR_10]], offset = 0>
+// CHECK: #[[$ATTR_15:.+]] = #llvm.tbaa_tag<base_type = #[[$ATTR_11]], access_type = #[[$ATTR_11]], offset = 0>
// CHECK: func.func @test1(
-// CHECK: %[[VAL_5:.*]] = fir.load %{{.*}} : !fir.ref<f32>
-// CHECK: fir.store %{{.*}} : !fir.ref<f32>
+// CHECK: %[[VAL_5:.*]] = fir.load %{{.*}} {tbaa = [#[[TARGETTAG]]]} : !fir.ref<f32>
+// CHECK: fir.store %{{.*}} {tbaa = [#[[TARGETTAG]]]} : !fir.ref<f32>
// CHECK: %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope
// CHECK: %[[VAL_9:.*]] = fir.load %{{.*}} {tbaa = [#[[$ATTR_12]]]} : !fir.ref<f32>
// CHECK: fir.store %{{.*}} {tbaa = [#[[$ATTR_13]]]} : !fir.ref<f32>
@@ -58,102 +63,6 @@ func.func @test1(%arg0: !fir.ref<f32> {fir.bindc_name = "x", fir.target}, %arg1:
// -----
-// Check that without proper fir.dummy_scope placement
-// we just put everything into the root scope.
-
-// CHECK: #[[$ATTR_16:.+]] = #llvm.tbaa_root<id = "Flang function root test2">
-// CHECK: #[[$ATTR_17:.+]] = #llvm.tbaa_type_desc<id = "any access", members = {<#tbaa_root, 0>}>
-// CHECK: #[[$ATTR_18:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#tbaa_type_desc, 0>}>
-// CHECK: #[[$ATTR_19:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data", members = {<#tbaa_type_desc1, 0>}>
-// CHECK: #[[$ATTR_20:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestEy", members = {<#tbaa_type_desc2, 0>}>
-// CHECK: #[[$ATTR_21:.+]] = #llvm.tbaa_type_desc<id = "dummy arg data/_QFtestEx", members = {<#tbaa_type_desc2, 0>}>
-// CHECK: #[[$ATTR_22:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc3, access_type = #tbaa_type_desc3, offset = 0>
-// CHECK: #[[$ATTR_23:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc4, access_type = #tbaa_type_desc4, offset = 0>
-// CHECK: func.func @test2(
-// CHECK: %[[VAL_4:.*]] = fir.load %{{.*}} {tbaa = [#[[$ATTR_22]]]} : !fir.ref<f32>
-// CHECK: fir.store %{{.*}} {tbaa = [#[[$ATTR_23]]]} : !fir.ref<f32>
-// CHECK: %[[VAL_5:.*]] = fir.declare
-// CHECK: %[[VAL_6:.*]] = fir.declare
-// CHECK: %[[VAL_7:.*]] = fir.load %{{.*}} {tbaa = [#[[$ATTR_22]]]} : !fir.ref<f32>
-// CHECK: fir.store %{{.*}} {tbaa = [#[[$ATTR_23]]]} : !fir.ref<f32>
-func.func @test2(%arg0: !fir.ref<f32> {fir.bindc_name = "x"}, %arg1: !fir.ref<f32> {fir.bindc_name = "y"}) {
- %0 = fir.declare %arg0 {uniq_name = "_QFtestEx"} : (!fir.ref<f32>) -> !fir.ref<f32>
- %1 = fir.declare %arg1 {uniq_name = "_QFtestEy"} : (!fir.ref<f32>) -> !fir.ref<f32>
- %2 = fir.load %1 : !fir.ref<f32>
- fir.store %2 to %0 : !fir.ref<f32>
- %3 = fir.declare %0 {uniq_name = "_QFtestFinnerEx"} : (!fir.ref<f32>) -> !fir.ref<f32>
- %4 = fir.declare %1 {uniq_name = "_QFtestFinnerEy"} : (!fir.ref<f32>) -> !fir.ref<f32>
- %5 = fir.load %4 : !fir.ref<f32>
- fir.store %5 to %3 : !fir.ref<f32>
- return
-}
-
-// -----
-
-// module test
-// real :: x, y
-// contains
-// subroutine caller
-// x = y ! the load/store go to the root scope
-// call callee
-// end subroutine caller
-// subroutine callee
-// x = y ! the load/store go to the root scope
-// ! Since there are no dummy arguments in callee,
-// ! it is better to put the load/store into the
-// ! enclosing root scope, so that they can be
-// ! disambiguated using TBAA with the loads/stores
-// ! in the enclosing scope.
-// end subroutine callee
-// end module test
-
-// CHECK: #[[$ATTR_24:.+]] = #llvm.tbaa_root<id = "Flang function root _QMtestPcaller">
-// CHECK: #[[$ATTR_25:.+]] = #llvm.tbaa_type_desc<id = "any access", members = {<#tbaa_root, 0>}>
-// CHECK: #[[$ATTR_26:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#tbaa_type_desc, 0>}>
-// CHECK: #[[$ATTR_27:.+]] = #llvm.tbaa_type_desc<id = "global data", members = {<#tbaa_type_desc1, 0>}>
-// CHECK: #[[$ATTR_28:.+]] = #llvm.tbaa_type_desc<id = "global data/_QMtestEy", members = {<#tbaa_type_desc2, 0>}>
-// CHECK: #[[$ATTR_29:.+]] = #llvm.tbaa_type_desc<id = "global data/_QMtestEx", members = {<#tbaa_type_desc2, 0>}>
-// CHECK: #[[$ATTR_30:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc3, access_type = #tbaa_type_desc3, offset = 0>
-// CHECK: #[[$ATTR_31:.+]] = #llvm.tbaa_tag<base_type = #tbaa_type_desc4, access_type = #tbaa_type_desc4, offset = 0>
-// CHECK: func.func @_QMtestPcaller() {
-// CHECK: %[[VAL_0:.*]] = fir.address_of(@_QMtestEx) : !fir.ref<f32>
-// CHECK: %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "_QMtestEx"} : (!fir.ref<f32>) -> !fir.ref<f32>
-// CHECK: %[[VAL_2:.*]] = fir.address_of(@_QMtestEy) : !fir.ref<f32>
-// CHECK: %[[VAL_3:.*]...
[truncated]
|
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.
Makes sense to me. Does it work ok with LLVM inlining?
Please wait for @tblah who knows much more than me about TBAA.
flang/test/Transforms/tbaa3.fir
Outdated
// | |- "global data/_QMdataEglob" | ||
// | |- "global data/_QMdataEglobt" | ||
// | | ||
// |- "direct data" - "direct data/_QMdataEgloba" |
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.
nit: extra - "direct data/_QMdataEgloba"
?
// | | ||
// |- "allocated data/_QFtest1Elocal" | ||
// |- "allocated data/_QFtest1Elocalt" | ||
// |- "allocated data/_QFtest1Elocala" | ||
// |- "allocated data/_QFtest1Elocalat" |
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 wonder if creating tags for each variables in "allocated data" and "global data" really makes a difference. I would imagine LLVM is already clever enough to know/deduce that two global addresses cannot alias if they do not come from the same global (and the same with alloca). I am not against it, but I imagine this is growing the metadata size a bit.
Also, for common block, will it create different tags for each common block members, or just one with the common block global name? Since they technically share the same storage, it would be safer to have a single tag for them I think. Same with local/global equivalences, but here I think we should probably fall into the POINTER cases currently (worth adding a test though).
I totally understand why adding tags for each variables makes a difference in the "direct data" and "dummy arg data" cases.
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 wonder if creating tags for each variables in "allocated data" and "global data" really makes a difference. I would imagine LLVM is already clever enough to know/deduce that two global addresses cannot alias if they do not come from the same global (and the same with alloca). I am not against it, but I imagine this is growing the metadata size a bit.
I think it does not make a difference, indeed. I will experiment with this in a separate PR. Thanks for noticing it!
Also, for common block, will it create different tags for each common block members, or just one with the common block global name? Since they technically share the same storage, it would be safer to have a single tag for them I think. Same with local/global equivalences, but here I think we should probably fall into the POINTER cases currently (worth adding a test though).
I will add a new test for common and equivalence. In short:
- Yes, we will create a single type descriptor and a tag for all member of the same common block.
- We will also create a single type descriptor and a tag for all member of an equivalence, though, accesses of local equivalences won't have any tag unless
--local-alloc-tbaa
experimental option is enabled.
I want to try to enable --local-alloc-tbaa
by default as well, but I will have to make sure WRF and other tests pass.
Almost missed the question :) Yes, it should work same way with LLVM inlining as before, but that does not mean our current TBAA approach is valid right now. Our TBAA is invalid for the following case:
Consider the case where
So it says that the first and last stores do not alias, which is not true. |
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 the new tests and answers, looks great to me
Thanks for this reorganization Slava. I'm worried about the "'Target' hole for dummy arguments" section of the aliasing documentation. If I understand correctly, a dummy argument which does not have the TARGET attribute could still be associated with an actual argument which does, and so it might alias with any variable with the POINTER attribute. So I think POINTER variables still need to be added as "any data". |
Thank you for the review, Tom. When a dummy argument does not have TARGET attribute and the underlying (sub)object is modified then it can be done only through referencing this dummy argument. If such a dummy argument is passed as an actual argument for associating it with the callee's dummy TARGET argument, then all the accesses to the callee's dummy argument will be attributed with TBAA having a different TBAA root (for the callee function), so the accesses within the callee and withing the caller will have disjoint TBAA that means MayAlias. So even if LLVM inlines the callee, I believe, the TBAA will be conservatively correct for such case. |
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 the clarification Slava. LGTM
This patch produces the following TBAA tree for a function: ``` Function root | "any access" | |- "descriptor member" |- "any data access" | |- "dummy arg data" |- "target data" | |- "allocated data" |- "direct data" |- "global data" ``` The TBAA tags are assigned using the following logic: * All POINTER variables point to the root of "target data". * Dummy arguments without POINTER/TARGET point to their leafs under "dummy arg data". * Dummy arguments with TARGET point to the root of "target data". * Global variables without descriptors point to their leafs under "global data" (including the ones with TARGET). * Global variables with descriptors point to their leafs under "direct data" (including the ones with TARGET). * Locally allocated variables point to their leafs under "allocated data" (including the ones with TARGET). This change makes it possible to disambiguate globals like: ``` module data real, allocatable :: a(:) real, allocatable, target :: b(:) end ``` Indeed, two direct references to global vars cannot alias even if any/both of them have TARGET attribute. In addition, the dummy arguments without POINTER/TARGET cannot alias any other variable even with POINTER/TARGET. This was not expressed in TBAA before this change. As before, any "unknown" memory references (such as with Indirect source, as classified by FIR alias analysis) may alias with anything, as long as they point to the root of "any access". Please think of the counterexamples for which this structure may not work.
This patch produces the following TBAA tree for a function: ``` Function root | "any access" | |- "descriptor member" |- "any data access" | |- "dummy arg data" |- "target data" | |- "allocated data" |- "direct data" |- "global data" ``` The TBAA tags are assigned using the following logic: * All POINTER variables point to the root of "target data". * Dummy arguments without POINTER/TARGET point to their leafs under "dummy arg data". * Dummy arguments with TARGET point to the root of "target data". * Global variables without descriptors point to their leafs under "global data" (including the ones with TARGET). * Global variables with descriptors point to their leafs under "direct data" (including the ones with TARGET). * Locally allocated variables point to their leafs under "allocated data" (including the ones with TARGET). This change makes it possible to disambiguate globals like: ``` module data real, allocatable :: a(:) real, allocatable, target :: b(:) end ``` Indeed, two direct references to global vars cannot alias even if any/both of them have TARGET attribute. In addition, the dummy arguments without POINTER/TARGET cannot alias any other variable even with POINTER/TARGET. This was not expressed in TBAA before this change. As before, any "unknown" memory references (such as with Indirect source, as classified by FIR alias analysis) may alias with anything, as long as they point to the root of "any access". Please think of the counterexamples for which this structure may not work.
This patch produces the following TBAA tree for a function: ``` Function root | "any access" | |- "descriptor member" |- "any data access" | |- "dummy arg data" |- "target data" | |- "allocated data" |- "direct data" |- "global data" ``` The TBAA tags are assigned using the following logic: * All POINTER variables point to the root of "target data". * Dummy arguments without POINTER/TARGET point to their leafs under "dummy arg data". * Dummy arguments with TARGET point to the root of "target data". * Global variables without descriptors point to their leafs under "global data" (including the ones with TARGET). * Global variables with descriptors point to their leafs under "direct data" (including the ones with TARGET). * Locally allocated variables point to their leafs under "allocated data" (including the ones with TARGET). This change makes it possible to disambiguate globals like: ``` module data real, allocatable :: a(:) real, allocatable, target :: b(:) end ``` Indeed, two direct references to global vars cannot alias even if any/both of them have TARGET attribute. In addition, the dummy arguments without POINTER/TARGET cannot alias any other variable even with POINTER/TARGET. This was not expressed in TBAA before this change. As before, any "unknown" memory references (such as with Indirect source, as classified by FIR alias analysis) may alias with anything, as long as they point to the root of "any access". Please think of the counterexamples for which this structure may not work.
This patch produces the following TBAA tree for a function: ``` Function root | "any access" | |- "descriptor member" |- "any data access" | |- "dummy arg data" |- "target data" | |- "allocated data" |- "direct data" |- "global data" ``` The TBAA tags are assigned using the following logic: * All POINTER variables point to the root of "target data". * Dummy arguments without POINTER/TARGET point to their leafs under "dummy arg data". * Dummy arguments with TARGET point to the root of "target data". * Global variables without descriptors point to their leafs under "global data" (including the ones with TARGET). * Global variables with descriptors point to their leafs under "direct data" (including the ones with TARGET). * Locally allocated variables point to their leafs under "allocated data" (including the ones with TARGET). This change makes it possible to disambiguate globals like: ``` module data real, allocatable :: a(:) real, allocatable, target :: b(:) end ``` Indeed, two direct references to global vars cannot alias even if any/both of them have TARGET attribute. In addition, the dummy arguments without POINTER/TARGET cannot alias any other variable even with POINTER/TARGET. This was not expressed in TBAA before this change. As before, any "unknown" memory references (such as with Indirect source, as classified by FIR alias analysis) may alias with anything, as long as they point to the root of "any access". Please think of the counterexamples for which this structure may not work.
This patch produces the following TBAA tree for a function: ``` Function root | "any access" | |- "descriptor member" |- "any data access" | |- "dummy arg data" |- "target data" | |- "allocated data" |- "direct data" |- "global data" ``` The TBAA tags are assigned using the following logic: * All POINTER variables point to the root of "target data". * Dummy arguments without POINTER/TARGET point to their leafs under "dummy arg data". * Dummy arguments with TARGET point to the root of "target data". * Global variables without descriptors point to their leafs under "global data" (including the ones with TARGET). * Global variables with descriptors point to their leafs under "direct data" (including the ones with TARGET). * Locally allocated variables point to their leafs under "allocated data" (including the ones with TARGET). This change makes it possible to disambiguate globals like: ``` module data real, allocatable :: a(:) real, allocatable, target :: b(:) end ``` Indeed, two direct references to global vars cannot alias even if any/both of them have TARGET attribute. In addition, the dummy arguments without POINTER/TARGET cannot alias any other variable even with POINTER/TARGET. This was not expressed in TBAA before this change. As before, any "unknown" memory references (such as with Indirect source, as classified by FIR alias analysis) may alias with anything, as long as they point to the root of "any access". Please think of the counterexamples for which this structure may not work.
This patch produces the following TBAA tree for a function:
The TBAA tags are assigned using the following logic:
leafs under "dummy arg data".
"global data" (including the ones with TARGET).
"direct data" (including the ones with TARGET).
"allocated data" (including the ones with TARGET).
This change makes it possible to disambiguate globals like:
Indeed, two direct references to global vars cannot alias
even if any/both of them have TARGET attribute.
In addition, the dummy arguments without POINTER/TARGET cannot alias
any other variable even with POINTER/TARGET. This was not expressed
in TBAA before this change.
As before, any "unknown" memory references (such as with Indirect
source, as classified by FIR alias analysis) may alias with
anything, as long as they point to the root of "any access".
Please think of the counterexamples for which this structure
may not work.