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
10 changes: 7 additions & 3 deletions flang/docs/Aliasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,18 @@ print *, target
end
```

Optimizations assume that Cray pointers do not alias any other variables.
In the above example, it is assumed that `handle` and `target` do not alias,
and optimizations will treat them as separate entities.
By default, optimizations assume that Cray pointers do not alias any other
variables. In the above example, it is assumed that `handle` and `target` do
not alias, and optimizations will treat them as separate entities.

In order to disable optimizations that assume that there is no aliasing between
Cray pointer targets and entities they alias with, add the TARGET attribute to
variables aliasing with a Cray pointer (the `target` variable in this example).

There is also a flag `-mmlir -funsafe-cray-pointers` which causes the compiler
to assume that cray pointers alias with all data whether or not it has the
TARGET attribute.

## Type considerations

Pointers with distinct types may alias so long as their types are
Expand Down
11 changes: 10 additions & 1 deletion flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct AliasAnalysis {
Unknown);

/// Attributes of the memory source object.
ENUM_CLASS(Attribute, Target, Pointer, IntentIn);
ENUM_CLASS(Attribute, Target, Pointer, IntentIn, CrayPointer, CrayPointee);

// See
// https://discourse.llvm.org/t/rfc-distinguish-between-data-and-non-data-in-fir-alias-analysis/78759/1
Expand Down Expand Up @@ -161,6 +161,15 @@ struct AliasAnalysis {
/// Return true, if Pointer attribute is set.
bool isPointer() const;

/// Return true, if CrayPointer attribute is set.
bool isCrayPointer() const;

/// Return true, if CrayPointee attribute is set.
bool isCrayPointee() const;

/// Return true, if CrayPointer or CrayPointee attribute is set.
bool isCrayPointerOrPointee() const;

bool isDummyArgument() const;
bool isData() const;
bool isBoxData() const;
Expand Down
6 changes: 5 additions & 1 deletion flang/include/flang/Optimizer/Dialect/FIRAttr.td
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ def FIRvolatile : I32BitEnumAttrCaseBit<"fortran_volatile", 12, "volatile">;
def FIRHostAssoc : I32BitEnumAttrCaseBit<"host_assoc", 13>;
// Used inside parent procedure to flag variables host associated in internal procedure.
def FIRInternalAssoc : I32BitEnumAttrCaseBit<"internal_assoc", 14>;
// Used by alias analysis
def FIRcray_pointer : I32BitEnumAttrCaseBit<"cray_pointer", 15>;
def FIRcray_pointee : I32BitEnumAttrCaseBit<"cray_pointee", 16>;

def fir_FortranVariableFlagsEnum : I32BitEnumAttr<
"FortranVariableFlagsEnum",
"Fortran variable attributes",
[FIRnoAttributes, FIRallocatable, FIRasynchronous, FIRbind_c, FIRcontiguous,
FIRintent_in, FIRintent_inout, FIRintent_out, FIRoptional, FIRparameter,
FIRpointer, FIRtarget, FIRvalue, FIRvolatile, FIRHostAssoc, FIRInternalAssoc]> {
FIRpointer, FIRtarget, FIRvalue, FIRvolatile, FIRHostAssoc, FIRInternalAssoc,
FIRcray_pointer, FIRcray_pointee]> {
let separator = ", ";
let cppNamespace = "::fir";
let printBitEnumPrimaryGroups = 1;
Expand Down
14 changes: 14 additions & 0 deletions flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ def fir_FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
fir::FortranVariableFlagsEnum::pointer);
}

/// Is this variable a Cray pointer?
bool isCrayPointer() {
auto attrs = getFortranAttrs();
return attrs && bitEnumContainsAny(*attrs,
fir::FortranVariableFlagsEnum::cray_pointer);
}

/// Is this variable a Cray pointee?
bool isCrayPointee() {
auto attrs = getFortranAttrs();
return attrs && bitEnumContainsAny(*attrs,
fir::FortranVariableFlagsEnum::cray_pointee);
}

/// Is this variable a Fortran allocatable?
bool isAllocatable() {
auto attrs = getFortranAttrs();
Expand Down
5 changes: 5 additions & 0 deletions flang/lib/Lower/ConvertVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1806,6 +1806,9 @@ fir::FortranVariableFlagsAttr Fortran::lower::translateSymbolAttributes(
if (sym.test(Fortran::semantics::Symbol::Flag::CrayPointee)) {
// CrayPointee are represented as pointers.
flags = flags | fir::FortranVariableFlagsEnum::pointer;
// Still use the CrayPointee flag so that AliasAnalysis can handle these
// separately.
flags = flags | fir::FortranVariableFlagsEnum::cray_pointee;
return fir::FortranVariableFlagsAttr::get(mlirContext, flags);
}
const auto &attrs = sym.attrs();
Expand Down Expand Up @@ -1835,6 +1838,8 @@ fir::FortranVariableFlagsAttr Fortran::lower::translateSymbolAttributes(
flags = flags | fir::FortranVariableFlagsEnum::value;
if (attrs.test(Fortran::semantics::Attr::VOLATILE))
flags = flags | fir::FortranVariableFlagsEnum::fortran_volatile;
if (sym.test(Fortran::semantics::Symbol::Flag::CrayPointer))
flags = flags | fir::FortranVariableFlagsEnum::cray_pointer;
if (flags == fir::FortranVariableFlagsEnum::None)
return {};
return fir::FortranVariableFlagsAttr::get(mlirContext, flags);
Expand Down
31 changes: 31 additions & 0 deletions flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"

using namespace mlir;

#define DEBUG_TYPE "fir-alias-analysis"

llvm::cl::opt<bool> supportCrayPointers(
"funsafe-cray-pointers",
llvm::cl::desc("Support Cray POINTERs that ALIAS with non-TARGET data"),
llvm::cl::init(false));

// Inspect for value-scoped Allocate effects and determine whether
// 'candidate' is a new allocation. Returns SourceKind::Allocate if a
// MemAlloc effect is attached
Expand Down Expand Up @@ -60,6 +66,10 @@ getAttrsFromVariable(fir::FortranVariableOpInterface var) {
attrs.set(fir::AliasAnalysis::Attribute::Pointer);
if (var.isIntentIn())
attrs.set(fir::AliasAnalysis::Attribute::IntentIn);
if (var.isCrayPointer())
attrs.set(fir::AliasAnalysis::Attribute::CrayPointer);
if (var.isCrayPointee())
attrs.set(fir::AliasAnalysis::Attribute::CrayPointee);

return attrs;
}
Expand Down Expand Up @@ -138,6 +148,18 @@ bool AliasAnalysis::Source::isPointer() const {
return attributes.test(Attribute::Pointer);
}

bool AliasAnalysis::Source::isCrayPointee() const {
return attributes.test(Attribute::CrayPointee);
}

bool AliasAnalysis::Source::isCrayPointer() const {
return attributes.test(Attribute::CrayPointer);
}

bool AliasAnalysis::Source::isCrayPointerOrPointee() const {
return isCrayPointer() || isCrayPointee();
}

bool AliasAnalysis::Source::isDummyArgument() const {
if (auto v = origin.u.dyn_cast<mlir::Value>()) {
return fir::isDummyArgument(v);
Expand Down Expand Up @@ -224,6 +246,15 @@ AliasResult AliasAnalysis::alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,
return AliasResult::MayAlias;
}

// Cray pointers/pointees can alias with anything via LOC.
if (supportCrayPointers) {
if (lhsSrc.isCrayPointerOrPointee() || rhsSrc.isCrayPointerOrPointee()) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing because of Cray pointer/pointee\n");
return AliasResult::MayAlias;
}
}

if (lhsSrc.kind == rhsSrc.kind) {
// If the kinds and origins are the same, then lhs and rhs must alias unless
// either source is approximate. Approximate sources are for parts of the
Expand Down
60 changes: 60 additions & 0 deletions flang/test/Analysis/AliasAnalysis/alias-analysis-cray-pointers.fir
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Check that cray pointers might alias with everything.

// RUN: fir-opt %s -funsafe-cray-pointers -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -mlir-disable-threading 2>&1 | FileCheck %s
// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -mlir-disable-threading 2>&1 | FileCheck --check-prefix=DEFAULT %s

// Fortran source:
// subroutine test()
// real :: a, b
// pointer(p, a)
// p = loc(b)
// end subroutine

// CHECK-LABEL: Testing : "_QPtest"
// CHECK-DAG: p#0 <-> b#0: MayAlias
// CHECK-DAG: p#1 <-> b#0: MayAlias
// CHECK-DAG: p#0 <-> b#1: MayAlias
// CHECK-DAG: p#1 <-> b#1: MayAlias
// CHECK-DAG: p#0 <-> a#0: MayAlias
// CHECK-DAG: p#1 <-> a#0: MayAlias
// CHECK-DAG: b#0 <-> a#0: MayAlias
// CHECK-DAG: b#1 <-> a#0: MayAlias
// CHECK-DAG: p#0 <-> a#1: MayAlias
// CHECK-DAG: p#1 <-> a#1: MayAlias
// CHECK-DAG: b#0 <-> a#1: MayAlias
// CHECK-DAG: b#1 <-> a#1: MayAlias

// By default, alias analysis assumes that cray pointers do not alias with
// non-target data. See flang/docs/Aliasing.md.
// DEFAULT-LABEL: Testing : "_QPtest"
// DEFAULT-DAG: p#0 <-> b#0: NoAlias
// DEFAULT-DAG: p#1 <-> b#0: NoAlias
// DEFAULT-DAG: p#0 <-> b#1: NoAlias
// DEFAULT-DAG: p#1 <-> b#1: NoAlias
// DEFAULT-DAG: p#0 <-> a#0: NoAlias
// DEFAULT-DAG: p#1 <-> a#0: NoAlias
// DEFAULT-DAG: b#0 <-> a#0: NoAlias
// DEFAULT-DAG: b#1 <-> a#0: NoAlias
// DEFAULT-DAG: p#0 <-> a#1: NoAlias
// DEFAULT-DAG: p#1 <-> a#1: NoAlias
// DEFAULT-DAG: b#0 <-> a#1: NoAlias
// DEFAULT-DAG: b#1 <-> a#1: NoAlias

func.func @_QPtest() {
%0 = fir.alloca !fir.box<!fir.ptr<f32>>
%1 = fir.dummy_scope : !fir.dscope
%2 = fir.alloca i64 {bindc_name = "p", uniq_name = "_QFtestEp"}
%3:2 = hlfir.declare %2 {test.ptr = "p", fortran_attrs = #fir.var_attrs<cray_pointer>, uniq_name = "_QFtestEp"} : (!fir.ref<i64>) -> (!fir.ref<i64>, !fir.ref<i64>)
%4 = fir.alloca f32 {bindc_name = "b", uniq_name = "_QFtestEb"}
%5:2 = hlfir.declare %4 {test.ptr = "b", uniq_name = "_QFtestEb"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
%6:2 = hlfir.declare %0 {test.ptr = "a", fortran_attrs = #fir.var_attrs<pointer, cray_pointee>, uniq_name = "_QFtestEa"} : (!fir.ref<!fir.box<!fir.ptr<f32>>>) -> (!fir.ref<!fir.box<!fir.ptr<f32>>>, !fir.ref<!fir.box<!fir.ptr<f32>>>)
%7 = fir.zero_bits !fir.ptr<f32>
%8 = fir.embox %7 : (!fir.ptr<f32>) -> !fir.box<!fir.ptr<f32>>
fir.store %8 to %6#0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
%9 = fir.embox %5#0 : (!fir.ref<f32>) -> !fir.box<f32>
%10 = fir.box_addr %9 : (!fir.box<f32>) -> !fir.ref<f32>
%11 = fir.convert %10 : (!fir.ref<f32>) -> i64
hlfir.assign %11 to %3#0 : i64, !fir.ref<i64>
return
}

Loading