Skip to content

[mlir][OpenMP] Add copyprivate support to omp.single #80477

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

Merged
merged 3 commits into from
Feb 15, 2024
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
4 changes: 3 additions & 1 deletion flang/lib/Lower/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2607,6 +2607,7 @@ genSingleOp(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::OmpClauseList &beginClauseList,
const Fortran::parser::OmpClauseList &endClauseList) {
llvm::SmallVector<mlir::Value> allocateOperands, allocatorOperands;
llvm::SmallVector<mlir::Value> copyPrivateVars;
mlir::UnitAttr nowaitAttr;

ClauseProcessor cp(converter, beginClauseList);
Expand All @@ -2620,7 +2621,8 @@ genSingleOp(Fortran::lower::AbstractConverter &converter,
OpWithBodyGenInfo(converter, currentLocation, eval)
.setGenNested(genNested)
.setClauses(&beginClauseList),
allocateOperands, allocatorOperands, nowaitAttr);
allocateOperands, allocatorOperands, copyPrivateVars,
/*copyPrivateFuncs=*/nullptr, nowaitAttr);
}

static mlir::omp::TaskOp
Expand Down
10 changes: 10 additions & 0 deletions mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -378,10 +378,16 @@ def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments]> {
master thread), in the context of its implicit task. The other threads
in the team, which do not execute the block, wait at an implicit barrier
at the end of the single construct unless a nowait clause is specified.

If copyprivate variables and functions are specified, then each thread
variable is updated with the variable value of the thread that executed
the single region, using the specified copy functions.
}];

let arguments = (ins Variadic<AnyType>:$allocate_vars,
Variadic<AnyType>:$allocators_vars,
Variadic<OpenMP_PointerLikeType>:$copyprivate_vars,
OptionalAttr<SymbolRefArrayAttr>:$copyprivate_funcs,
UnitAttr:$nowait);

let regions = (region AnyRegion:$region);
Expand All @@ -393,6 +399,10 @@ def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments]> {
$allocators_vars, type($allocators_vars)
) `)`
|`nowait` $nowait
|`copyprivate` `(`
custom<CopyPrivateVarList>(
$copyprivate_vars, type($copyprivate_vars), $copyprivate_funcs
) `)`
) $region attr-dict
}];
let hasVerifier = 1;
Expand Down
107 changes: 106 additions & 1 deletion mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,110 @@ static LogicalResult verifyReductionVarList(Operation *op,
return success();
}

//===----------------------------------------------------------------------===//
// Parser, printer and verifier for CopyPrivateVarList
//===----------------------------------------------------------------------===//

/// copyprivate-entry-list ::= copyprivate-entry
/// | copyprivate-entry-list `,` copyprivate-entry
/// copyprivate-entry ::= ssa-id `->` symbol-ref `:` type
static ParseResult parseCopyPrivateVarList(
OpAsmParser &parser,
SmallVectorImpl<OpAsmParser::UnresolvedOperand> &operands,
SmallVectorImpl<Type> &types, ArrayAttr &copyPrivateSymbols) {
SmallVector<SymbolRefAttr> copyPrivateFuncsVec;
if (failed(parser.parseCommaSeparatedList([&]() {
if (parser.parseOperand(operands.emplace_back()) ||
parser.parseArrow() ||
parser.parseAttribute(copyPrivateFuncsVec.emplace_back()) ||
parser.parseColonType(types.emplace_back()))
return failure();
return success();
})))
return failure();
SmallVector<Attribute> copyPrivateFuncs(copyPrivateFuncsVec.begin(),
copyPrivateFuncsVec.end());
copyPrivateSymbols = ArrayAttr::get(parser.getContext(), copyPrivateFuncs);
return success();
}

/// Print CopyPrivate clause
static void printCopyPrivateVarList(OpAsmPrinter &p, Operation *op,
OperandRange copyPrivateVars,
TypeRange copyPrivateTypes,
std::optional<ArrayAttr> copyPrivateFuncs) {
if (!copyPrivateFuncs.has_value())
return;
llvm::interleaveComma(
llvm::zip(copyPrivateVars, *copyPrivateFuncs, copyPrivateTypes), p,
[&](const auto &args) {
p << std::get<0>(args) << " -> " << std::get<1>(args) << " : "
<< std::get<2>(args);
});
}

/// Verifies CopyPrivate Clause
static LogicalResult
verifyCopyPrivateVarList(Operation *op, OperandRange copyPrivateVars,
std::optional<ArrayAttr> copyPrivateFuncs) {
size_t copyPrivateFuncsSize =
copyPrivateFuncs.has_value() ? copyPrivateFuncs->size() : 0;
if (copyPrivateFuncsSize != copyPrivateVars.size())
return op->emitOpError() << "inconsistent number of copyPrivate vars (= "
<< copyPrivateVars.size()
<< ") and functions (= " << copyPrivateFuncsSize
<< "), both must be equal";
if (!copyPrivateFuncs.has_value())
return success();

for (auto copyPrivateVarAndFunc :
llvm::zip(copyPrivateVars, *copyPrivateFuncs)) {
auto symbolRef =
llvm::cast<SymbolRefAttr>(std::get<1>(copyPrivateVarAndFunc));
std::optional<std::variant<mlir::func::FuncOp, mlir::LLVM::LLVMFuncOp>>
funcOp;
if (mlir::func::FuncOp mlirFuncOp =
SymbolTable::lookupNearestSymbolFrom<mlir::func::FuncOp>(op,
symbolRef))
funcOp = mlirFuncOp;
else if (mlir::LLVM::LLVMFuncOp llvmFuncOp =
SymbolTable::lookupNearestSymbolFrom<mlir::LLVM::LLVMFuncOp>(
op, symbolRef))
funcOp = llvmFuncOp;

auto getNumArguments = [&] {
return std::visit([](auto &f) { return f.getNumArguments(); }, *funcOp);
};

auto getArgumentType = [&](unsigned i) {
return std::visit([i](auto &f) { return f.getArgumentTypes()[i]; },
*funcOp);
};

if (!funcOp)
return op->emitOpError() << "expected symbol reference " << symbolRef
<< " to point to a copy function";

if (getNumArguments() != 2)
return op->emitOpError()
<< "expected copy function " << symbolRef << " to have 2 operands";

Type argTy = getArgumentType(0);
if (argTy != getArgumentType(1))
return op->emitOpError() << "expected copy function " << symbolRef
<< " arguments to have the same type";

Type varType = std::get<0>(copyPrivateVarAndFunc).getType();
if (argTy != varType)
return op->emitOpError()
<< "expected copy function arguments' type (" << argTy
<< ") to be the same as copyprivate variable's type (" << varType
<< ")";
}

return success();
}

//===----------------------------------------------------------------------===//
// Parser, printer and verifier for DependVarList
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1072,7 +1176,8 @@ LogicalResult SingleOp::verify() {
return emitError(
"expected equal sizes for allocate and allocator variables");

return success();
return verifyCopyPrivateVarList(*this, getCopyprivateVars(),
getCopyprivateFuncs());
}

//===----------------------------------------------------------------------===//
Expand Down
58 changes: 57 additions & 1 deletion mlir/test/Dialect/OpenMP/invalid.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,63 @@ func.func @omp_single(%data_var : memref<i32>) -> () {
// expected-error @below {{expected equal sizes for allocate and allocator variables}}
"omp.single" (%data_var) ({
omp.barrier
}) {operandSegmentSizes = array<i32: 1,0>} : (memref<i32>) -> ()
}) {operandSegmentSizes = array<i32: 1,0,0>} : (memref<i32>) -> ()
return
}

// -----

func.func @omp_single_copyprivate(%data_var : memref<i32>) -> () {
// expected-error @below {{inconsistent number of copyPrivate vars (= 1) and functions (= 0), both must be equal}}
"omp.single" (%data_var) ({
omp.barrier
}) {operandSegmentSizes = array<i32: 0,0,1>} : (memref<i32>) -> ()
return
}

// -----

func.func @omp_single_copyprivate(%data_var : memref<i32>) -> () {
// expected-error @below {{expected symbol reference @copy_func to point to a copy function}}
omp.single copyprivate(%data_var -> @copy_func : memref<i32>) {
omp.barrier
}
return
}

// -----

func.func private @copy_func(memref<i32>)

func.func @omp_single_copyprivate(%data_var : memref<i32>) -> () {
// expected-error @below {{expected copy function @copy_func to have 2 operands}}
omp.single copyprivate(%data_var -> @copy_func : memref<i32>) {
omp.barrier
}
return
}

// -----

func.func private @copy_func(memref<i32>, memref<f32>)

func.func @omp_single_copyprivate(%data_var : memref<i32>) -> () {
// expected-error @below {{expected copy function @copy_func arguments to have the same type}}
omp.single copyprivate(%data_var -> @copy_func : memref<i32>) {
omp.barrier
}
return
}

// -----

func.func private @copy_func(memref<f32>, memref<f32>)

func.func @omp_single_copyprivate(%data_var : memref<i32>) -> () {
// expected-error @below {{expected copy function arguments' type ('memref<f32>') to be the same as copyprivate variable's type ('memref<i32>')}}
omp.single copyprivate(%data_var -> @copy_func : memref<i32>) {
omp.barrier
}
return
}

Expand Down
17 changes: 17 additions & 0 deletions mlir/test/Dialect/OpenMP/ops.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,23 @@ func.func @omp_single_multiple_blocks() {
return
}

func.func private @copy_i32(memref<i32>, memref<i32>)

// CHECK-LABEL: func @omp_single_copyprivate
func.func @omp_single_copyprivate(%data_var: memref<i32>) {
omp.parallel {
// CHECK: omp.single copyprivate(%{{.*}} -> @copy_i32 : memref<i32>) {
omp.single copyprivate(%data_var -> @copy_i32 : memref<i32>) {
"test.payload"() : () -> ()
// CHECK: omp.terminator
omp.terminator
}
// CHECK: omp.terminator
omp.terminator
}
return
}

// CHECK-LABEL: @omp_task
// CHECK-SAME: (%[[bool_var:.*]]: i1, %[[i64_var:.*]]: i64, %[[i32_var:.*]]: i32, %[[data_var:.*]]: memref<i32>)
func.func @omp_task(%bool_var: i1, %i64_var: i64, %i32_var: i32, %data_var: memref<i32>) {
Expand Down