diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp index 5cfbdc33285f9..4e788f75d9d77 100644 --- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp +++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp @@ -73,7 +73,12 @@ class PassState { } void processFunctionScopes(mlir::func::FuncOp func); - fir::DummyScopeOp getDeclarationScope(fir::DeclareOp declareOp); + // For the given fir.declare returns the dominating fir.dummy_scope + // operation. + fir::DummyScopeOp getDeclarationScope(fir::DeclareOp declareOp) const; + // For the given fir.declare returns the outermost fir.dummy_scope + // in the current function. + fir::DummyScopeOp getOutermostScope(fir::DeclareOp declareOp) const; private: mlir::DominanceInfo &domInfo; @@ -119,9 +124,8 @@ void PassState::processFunctionScopes(mlir::func::FuncOp func) { } } -// For the given fir.declare returns the dominating fir.dummy_scope -// operation. -fir::DummyScopeOp PassState::getDeclarationScope(fir::DeclareOp declareOp) { +fir::DummyScopeOp +PassState::getDeclarationScope(fir::DeclareOp declareOp) const { auto func = declareOp->getParentOfType(); assert(func && "fir.declare does not have parent func.func"); auto &scopeOps = sortedScopeOperations.at(func); @@ -132,6 +136,15 @@ fir::DummyScopeOp PassState::getDeclarationScope(fir::DeclareOp declareOp) { return nullptr; } +fir::DummyScopeOp PassState::getOutermostScope(fir::DeclareOp declareOp) const { + auto func = declareOp->getParentOfType(); + assert(func && "fir.declare does not have parent func.func"); + auto &scopeOps = sortedScopeOperations.at(func); + if (!scopeOps.empty()) + return scopeOps[0]; + return nullptr; +} + class AddAliasTagsPass : public fir::impl::AddAliasTagsBase { public: void runOnOperation() override; @@ -279,6 +292,15 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op, else unknownAllocOp = true; + if (auto declOp = source.origin.instantiationPoint) { + // Use the outermost scope for local allocations, + // because using the innermost scope may result + // in incorrect TBAA, when calls are inlined in MLIR. + auto declareOp = mlir::dyn_cast(declOp); + assert(declareOp && "Instantiation point must be fir.declare"); + scopeOp = state.getOutermostScope(declareOp); + } + if (unknownAllocOp) { LLVM_DEBUG(llvm::dbgs().indent(2) << "WARN: unknown defining op for SourceKind::Allocate " << *op diff --git a/flang/test/Transforms/tbaa-for-local-vars.fir b/flang/test/Transforms/tbaa-for-local-vars.fir new file mode 100644 index 0000000000000..82058ffef290a --- /dev/null +++ b/flang/test/Transforms/tbaa-for-local-vars.fir @@ -0,0 +1,92 @@ +// Test TBAA tags assignment for access off the fir.declare +// placed in the middle of the routine (e.g. a temporary). +// Original example: +// module m +// type t +// real :: x +// end type t +// contains +// subroutine bar(this) +// class(t), intent(out) :: this +// this%x = 1.0 +// end subroutine bar +// function foo() result(res) +// type(t) :: res +// call bar(res) +// end function foo +// subroutine test(arg) +// type(t) :: var +// var = foo() +// arg = var%x +// end subroutine test +// end module m +// +// The calls were manually inlined in FIR with fir.save_result's +// destination operand being used instead of the temporary +// alloca of the result inside foo. Runtime calls were removed +// to reduce the test. +// The temporary function result fir.declare is then dominated +// by fir.dummy_scope of the foo function. If we use this scope +// to assign the TBAA local-alloc tag to the accesses of +// the temporary, then it won't conflict with the TBAA dummy tag +// assigned to the accesses of this argument of bar. +// That would be incorrect, because they access the same memory. +// Check that the local-alloc tag is placed into the outermost +// scope's TBAA tree. +// RUN: fir-opt --fir-add-alias-tags %s | FileCheck %s + +// CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_4:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_5:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_6:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_7:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_9:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_10:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_12:.+]] = #llvm.tbaa_tag +// CHECK: #[[$ATTR_13:.+]] = #llvm.tbaa_tag + +// CHECK-LABEL: func.func @_QMmPtest( +// CHECK-SAME: %[[ARG0:.*]]: !fir.ref {fir.bindc_name = "arg"}) { +// CHECK: %[[VAL_0:.*]] = arith.constant 1.000000e+00 : f32 +// CHECK: %[[VAL_1:.*]] = fir.alloca !fir.type<_QMmTt{x:f32}> {bindc_name = ".result"} +// CHECK: %[[VAL_2:.*]] = fir.dummy_scope : !fir.dscope +// CHECK: %[[VAL_3:.*]] = fir.declare %[[ARG0]] dummy_scope %[[VAL_2]] {uniq_name = "_QMmFtestEarg"} : (!fir.ref, !fir.dscope) -> !fir.ref +// CHECK: %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope +// CHECK: %[[VAL_7:.*]] = fir.declare %[[VAL_1]] {uniq_name = "_QMmFfooEres"} : (!fir.ref>) -> !fir.ref> +// CHECK: %[[VAL_8:.*]] = fir.embox %[[VAL_7]] : (!fir.ref>) -> !fir.box> +// CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_8]] : (!fir.box>) -> !fir.class> +// CHECK: %[[VAL_10:.*]] = fir.dummy_scope : !fir.dscope +// CHECK: %[[VAL_11:.*]] = fir.declare %[[VAL_9]] dummy_scope %[[VAL_10]] {fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFbarEthis"} : (!fir.class>, !fir.dscope) -> !fir.class> +// CHECK: %[[VAL_12:.*]] = fir.coordinate_of %[[VAL_11]], x : (!fir.class>) -> !fir.ref +// CHECK: fir.store %[[VAL_0]] to %[[VAL_12]] {tbaa = [#[[$ATTR_12]]]} : !fir.ref +// CHECK: %[[VAL_13:.*]] = fir.declare %[[VAL_1]] {uniq_name = ".tmp.func_result"} : (!fir.ref>) -> !fir.ref> +// CHECK: %[[VAL_14:.*]] = fir.coordinate_of %[[VAL_13]], x : (!fir.ref>) -> !fir.ref +// CHECK: %[[VAL_16:.*]] = fir.load %[[VAL_14]] {tbaa = [#[[$ATTR_13]]]} : !fir.ref +func.func @_QMmPtest(%arg0: !fir.ref {fir.bindc_name = "arg"}) { + %cst = arith.constant 1.000000e+00 : f32 + %0 = fir.alloca !fir.type<_QMmTt{x:f32}> {bindc_name = ".result"} + %1 = fir.dummy_scope : !fir.dscope + %2 = fir.declare %arg0 dummy_scope %1 {uniq_name = "_QMmFtestEarg"} : (!fir.ref, !fir.dscope) -> !fir.ref + %3 = fir.alloca !fir.type<_QMmTt{x:f32}> {bindc_name = "var", uniq_name = "_QMmFtestEvar"} + %4 = fir.declare %3 {uniq_name = "_QMmFtestEvar"} : (!fir.ref>) -> !fir.ref> + %5 = fir.dummy_scope : !fir.dscope + %6 = fir.declare %0 {uniq_name = "_QMmFfooEres"} : (!fir.ref>) -> !fir.ref> + %7 = fir.embox %6 : (!fir.ref>) -> !fir.box> + %8 = fir.convert %7 : (!fir.box>) -> !fir.class> + %9 = fir.dummy_scope : !fir.dscope + %10 = fir.declare %8 dummy_scope %9 {fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFbarEthis"} : (!fir.class>, !fir.dscope) -> !fir.class> + %14 = fir.coordinate_of %10, x : (!fir.class>) -> !fir.ref + fir.store %cst to %14 : !fir.ref + %15 = fir.declare %0 {uniq_name = ".tmp.func_result"} : (!fir.ref>) -> !fir.ref> + %16 = fir.coordinate_of %15, x : (!fir.ref>) -> !fir.ref + %17 = fir.coordinate_of %4, x : (!fir.ref>) -> !fir.ref + %18 = fir.load %16 : !fir.ref + fir.store %18 to %17 : !fir.ref + %19 = fir.load %17 : !fir.ref + fir.store %19 to %2 : !fir.ref + return +} + diff --git a/flang/test/Transforms/tbaa-with-dummy-scope2.fir b/flang/test/Transforms/tbaa-with-dummy-scope2.fir index 249471de458d3..fd711a4d70eb4 100644 --- a/flang/test/Transforms/tbaa-with-dummy-scope2.fir +++ b/flang/test/Transforms/tbaa-with-dummy-scope2.fir @@ -84,18 +84,17 @@ func.func @_QPtest2() attributes {noinline} { fir.store %c2_i32 to %2 : !fir.ref return } -// CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root -// CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_root // CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$ATTR_3:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$ATTR_4:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$ATTR_5:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$TARGETDATA_0:.+]] = #llvm.tbaa_type_desc}> -// CHECK: #[[$ATTR_6:.+]] = #llvm.tbaa_type_desc}> -// CHECK: #[[$TARGETDATA_1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_6:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$LOCAL_ATTR_0:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$ATTR_8:.+]] = #llvm.tbaa_type_desc}> -// CHECK: #[[$ATTR_7:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_7:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$ATTR_10:.+]] = #llvm.tbaa_tag // CHECK: #[[$LOCAL_ATTR_1:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$ATTR_9:.+]] = #llvm.tbaa_type_desc}>