Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
25 changes: 14 additions & 11 deletions flang/include/flang/Optimizer/Dialect/CUF/CUFOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ include "mlir/IR/BuiltinAttributes.td"
class cuf_Op<string mnemonic, list<Trait> traits>
: Op<CUFDialect, mnemonic, traits>;

def cuf_AllocOp : cuf_Op<"alloc", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc]>]> {
def cuf_AllocOp : cuf_Op<"alloc", [AttrSizedOperandSegments]> {
let summary = "Allocate an object on device";

let description = [{
Expand All @@ -47,7 +46,9 @@ def cuf_AllocOp : cuf_Op<"alloc", [AttrSizedOperandSegments,
cuf_DataAttributeAttr:$data_attr
);

let results = (outs fir_ReferenceType:$ptr);
// Value-scoped Allocate on the returned reference
let results =
(outs Res<fir_ReferenceType, "", [MemAlloc<DefaultResource>]>:$ptr);

let assemblyFormat = [{
$in_type (`(` $typeparams^ `:` type($typeparams) `)`)?
Expand Down Expand Up @@ -84,8 +85,7 @@ def cuf_FreeOp : cuf_Op<"free", [MemoryEffects<[MemFree]>]> {
let hasVerifier = 1;
}

def cuf_AllocateOp : cuf_Op<"allocate", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc<DefaultResource>]>]> {
def cuf_AllocateOp : cuf_Op<"allocate", [AttrSizedOperandSegments]> {
let summary = "Perform the device allocation of data of an allocatable";

let description = [{
Expand All @@ -94,12 +94,15 @@ def cuf_AllocateOp : cuf_Op<"allocate", [AttrSizedOperandSegments,
is initialized before with the standard flang runtime calls.
}];

let arguments = (ins Arg<fir_ReferenceType, "", [MemRead, MemWrite]>:$box,
Arg<Optional<AnyRefOrBoxType>, "", [MemWrite]>:$errmsg,
Optional<fir_ReferenceType>:$stream,
Arg<Optional<AnyRefOrBoxType>, "", [MemWrite]>:$pinned,
Arg<Optional<AnyRefOrBoxType>, "", [MemRead]>:$source,
cuf_DataAttributeAttr:$data_attr, UnitAttr:$hasStat);
// Value-scoped Allocate on the descriptor being allocated
let arguments =
(ins Arg<fir_ReferenceType,
"", [MemAlloc<DefaultResource>, MemRead, MemWrite]>:$box,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure, but I think MemAlloc<DefaultResource> does not do anything on the operand, and we have to keep the MemAlloc on the operation itself.

Unfortunately, "indirect" allocations are not that easy to express.

Copy link
Contributor

@jeanPerier jeanPerier Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An option here could be to revamp this operation a bit so that it returns a fir.box and that a fir.store is generated on top of it to store this result in the original fir.ref<box>.
This decomposition would allow having MemAlloc on the result fir.box and could allow mem2reg to directly link fir.load of the allocatable to this allocatable when possible (not sure how useful that is in the context of CUF).
The downside would be that the "naive" codegen would create an extra desc -> temp desc (new cuf.allocate codegen needs to generate a load to produce the new fir.box result) and temp desc -> copy (the extra fir.store from lowering). This should however rather be easy to remove if LLVM is not already doing it for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point! let me try that

Copy link
Contributor Author

@SusanTan SusanTan Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to revert the changes for cuf.allocate, because it's only used for fortran allocatables which always allocates data for the descriptor, and the descriptor can be distinguished by cuf.alloc and that should be enough for AA to figure out the allocation site.

Arg<Optional<AnyRefOrBoxType>, "", [MemWrite]>:$errmsg,
Optional<fir_ReferenceType>:$stream,
Arg<Optional<AnyRefOrBoxType>, "", [MemWrite]>:$pinned,
Arg<Optional<AnyRefOrBoxType>, "", [MemRead]>:$source,
cuf_DataAttributeAttr:$data_attr, UnitAttr:$hasStat);

let results = (outs AnyIntegerType:$stat);

Expand Down
22 changes: 22 additions & 0 deletions flang/test/Analysis/AliasAnalysis/cuf-alloc-source-kind.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// REQUIRES: asserts
// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -debug-only=fir-alias-analysis --mlir-disable-threading 2>&1 | FileCheck %s

// Verify that a CUF allocation is recognized as SourceKind::Allocate by
// fir::AliasAnalysis::getSource.

module {
func.func @_QQmain() attributes {fir.bindc_name = "TEST"} {
// Allocate two independent device arrays and tag the results; with
// value-scoped MemAlloc handling in AA, these should be classified as
// Allocate and not alias.
%a = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a1", data_attr = #cuf.cuda<device>, uniq_name = "_QFEa1", test.ptr = "cuf_alloc_a"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%b = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a2", data_attr = #cuf.cuda<device>, uniq_name = "_QFEa2", test.ptr = "cuf_alloc_b"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
return
}
}

// CHECK-LABEL: Testing : "_QQmain"
// Distinct allocations should not alias.
// CHECK: cuf_alloc_a#0 <-> cuf_alloc_b#0: NoAlias


24 changes: 24 additions & 0 deletions flang/test/Analysis/AliasAnalysis/cuf-allocate-source-kind.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// REQUIRES: asserts
// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -debug-only=fir-alias-analysis --mlir-disable-threading 2>&1 | FileCheck %s

// Verify that CUF allocation via cuf.allocate is recognized as
// SourceKind::Allocate by fir::AliasAnalysis::getSource on the box value.

module {
func.func @_QQmain() attributes {fir.bindc_name = "TEST"} {
// Create two independent device boxes and tag their refs.
%a = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a1", data_attr = #cuf.cuda<device>, uniq_name = "_QFEa1", test.ptr = "cuf_allocate_a"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%b = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a2", data_attr = #cuf.cuda<device>, uniq_name = "_QFEa2", test.ptr = "cuf_allocate_b"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
// Allocate device data for each descriptor; AA should classify the box
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think this test is redundant, because cuf-alloc-source-kind.mlir above already tests the same thing.

// values (tagged above) as Allocate sources and not alias.
%sa = cuf.allocate %a : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
%sb = cuf.allocate %b : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
return
}
}

// CHECK-LABEL: Testing : "_QQmain"
// Distinct allocations via cuf.allocate should not alias.
// CHECK: cuf_allocate_a#0 <-> cuf_allocate_b#0: NoAlias


Loading