Skip to content

Commit a4e63bb

Browse files
committed
[OpenACC][CIR] Implement beginning of 'copy' lowering for compute constructs
This is a partial implementation of the 'copy' lowering. It is missing 3 things, which are coming in future patches: 1- does not handle subscript/subarrays for emission as variables 2- does not handle member expressions for emissions as variables 3- does not handle modifier-list 1 and 2 are because of the complexity and should be split off into a separate patch. 3 is because it isn't clear how the IR is going to handle this, and I'd like to make sure it gets done 'all at once' when the IR is updated to handle these, so I'm pushing that off to the future. This DOES however handle the complexity of having a acc.copyin and acc.copyout, plus the additional complexity of the 'async' clause.
1 parent ad6bb70 commit a4e63bb

File tree

4 files changed

+428
-21
lines changed

4 files changed

+428
-21
lines changed

clang/lib/CIR/CodeGen/CIRGenOpenACCClause.h

Lines changed: 211 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "mlir/Dialect/Arith/IR/Arith.h"
1616
#include "mlir/Dialect/OpenACC/OpenACC.h"
17+
#include "llvm/ADT/TypeSwitch.h"
1718
namespace clang {
1819
// Simple type-trait to see if the first template arg is one of the list, so we
1920
// can tell whether to `if-constexpr` a bunch of stuff.
@@ -36,6 +37,76 @@ template <typename ToTest> constexpr bool isCombinedType = false;
3637
template <typename T>
3738
constexpr bool isCombinedType<CombinedConstructClauseInfo<T>> = true;
3839

40+
namespace {
41+
struct DataOperandInfo {
42+
mlir::Location beginLoc;
43+
mlir::Value varValue;
44+
llvm::StringRef name;
45+
mlir::ValueRange bounds;
46+
47+
DataOperandInfo(mlir::Location beginLoc, mlir::Value varValue,
48+
llvm::StringRef name, mlir::ValueRange bounds)
49+
: beginLoc(beginLoc), varValue(varValue), name(name), bounds(bounds) {}
50+
};
51+
52+
inline mlir::Value createIntExpr(CIRGen::CIRGenFunction &cgf,
53+
CIRGen::CIRGenBuilderTy &builder,
54+
const Expr *intExpr) {
55+
mlir::Value expr = cgf.emitScalarExpr(intExpr);
56+
mlir::Location exprLoc = cgf.cgm.getLoc(intExpr->getBeginLoc());
57+
58+
mlir::IntegerType targetType = mlir::IntegerType::get(
59+
&cgf.getMLIRContext(), cgf.getContext().getIntWidth(intExpr->getType()),
60+
intExpr->getType()->isSignedIntegerOrEnumerationType()
61+
? mlir::IntegerType::SignednessSemantics::Signed
62+
: mlir::IntegerType::SignednessSemantics::Unsigned);
63+
64+
auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
65+
exprLoc, targetType, expr);
66+
return conversionOp.getResult(0);
67+
}
68+
69+
// A helper function that gets the information from an operand to a data
70+
// clause, so that it can be used to emit the data operations.
71+
inline DataOperandInfo getDataOperandInfo(CIRGen::CIRGenFunction &cgf,
72+
CIRGen::CIRGenBuilderTy &builder,
73+
OpenACCDirectiveKind DK,
74+
const Expr *E) {
75+
// TODO: OpenACC: Cache was different enough as to need a separate
76+
// `ActOnCacheVar`, so we are going to need to do some investigations here
77+
// when it comes to implement this for cache.
78+
assert(DK != OpenACCDirectiveKind::Cache &&
79+
"Cache has different enough functionality we need to investigate "
80+
"whether this function works for it");
81+
const Expr *curVarExpr = E->IgnoreParenImpCasts();
82+
83+
mlir::Location exprLoc = cgf.cgm.getLoc(curVarExpr->getBeginLoc());
84+
llvm::SmallVector<mlir::Value> bounds;
85+
86+
// TODO: OpenACC: Assemble the list of bounds.
87+
if (isa<ArraySectionExpr, ArraySubscriptExpr>(curVarExpr)) {
88+
cgf.cgm.errorNYI(curVarExpr->getSourceRange(),
89+
"OpenACC data clause array subscript/section");
90+
return {exprLoc, {}, {}, bounds};
91+
}
92+
93+
// TODO: OpenACC: if this is a member expr, emit the VarPtrPtr correctly.
94+
if (const auto *ME = dyn_cast<MemberExpr>(curVarExpr)) {
95+
cgf.cgm.errorNYI(curVarExpr->getSourceRange(),
96+
"OpenACC Data clause member expr");
97+
return {exprLoc, {}, {}, bounds};
98+
}
99+
100+
// Sema has made sure that only 4 types of things can get here, array
101+
// subscript, array section, member expr, or DRE to a var decl (or the former
102+
// 3 wrapping a var-decl), so we should be able to assume this is right.
103+
const auto *DRE = cast<DeclRefExpr>(curVarExpr);
104+
const auto *VD = cast<VarDecl>(DRE->getFoundDecl()->getCanonicalDecl());
105+
return {exprLoc, cgf.emitDeclRefLValue(DRE).getPointer(), VD->getName(),
106+
bounds};
107+
}
108+
} // namespace
109+
39110
template <typename OpTy>
40111
class OpenACCClauseCIREmitter final
41112
: public OpenACCClauseVisitor<OpenACCClauseCIREmitter<OpTy>> {
@@ -54,6 +125,11 @@ class OpenACCClauseCIREmitter final
54125
SourceLocation dirLoc;
55126

56127
llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;
128+
// Keep track of the async-clause so that we can shortcut updating the data
129+
// operands async clauses.
130+
bool hasAsyncClause = false;
131+
// Keep track of the data operands so that we can update their async clauses.
132+
llvm::SmallVector<mlir::Operation *> dataOperands;
57133

58134
void setLastDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
59135
lastDeviceTypeValues.clear();
@@ -70,18 +146,7 @@ class OpenACCClauseCIREmitter final
70146
}
71147

72148
mlir::Value createIntExpr(const Expr *intExpr) {
73-
mlir::Value expr = cgf.emitScalarExpr(intExpr);
74-
mlir::Location exprLoc = cgf.cgm.getLoc(intExpr->getBeginLoc());
75-
76-
mlir::IntegerType targetType = mlir::IntegerType::get(
77-
&cgf.getMLIRContext(), cgf.getContext().getIntWidth(intExpr->getType()),
78-
intExpr->getType()->isSignedIntegerOrEnumerationType()
79-
? mlir::IntegerType::SignednessSemantics::Signed
80-
: mlir::IntegerType::SignednessSemantics::Unsigned);
81-
82-
auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
83-
exprLoc, targetType, expr);
84-
return conversionOp.getResult(0);
149+
return clang::createIntExpr(cgf, builder, intExpr);
85150
}
86151

87152
// 'condition' as an OpenACC grammar production is used for 'if' and (some
@@ -157,6 +222,103 @@ class OpenACCClauseCIREmitter final
157222
computeEmitter.Visit(&c);
158223
}
159224

225+
template <typename BeforeOpTy, typename AfterOpTy>
226+
void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,
227+
bool structured, bool implicit) {
228+
DataOperandInfo opInfo =
229+
getDataOperandInfo(cgf, builder, dirKind, varOperand);
230+
231+
// TODO: OpenACC: we should comprehend the 'modifier-list' here for the data
232+
// operand. At the moment, we don't have a uniform way to assign these
233+
// properly, and the dialect cannot represent anything other than 'readonly'
234+
// and 'zero' on copyin/copyout/create, so for now, we skip it.
235+
236+
auto beforeOp =
237+
builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,
238+
implicit, opInfo.name, opInfo.bounds);
239+
operation.getDataClauseOperandsMutable().append(beforeOp.getResult());
240+
241+
AfterOpTy afterOp;
242+
{
243+
mlir::OpBuilder::InsertionGuard guardCase(builder);
244+
builder.setInsertionPointAfter(operation);
245+
afterOp = builder.create<AfterOpTy>(opInfo.beginLoc, beforeOp.getResult(),
246+
opInfo.varValue, structured, implicit,
247+
opInfo.name, opInfo.bounds);
248+
}
249+
250+
// Set the 'rest' of the info for both operations.
251+
beforeOp.setDataClause(dataClause);
252+
afterOp.setDataClause(dataClause);
253+
254+
// Make sure we record these, so 'async' values can be updated later.
255+
dataOperands.push_back(beforeOp.getOperation());
256+
dataOperands.push_back(afterOp.getOperation());
257+
}
258+
259+
// Helper function that covers for the fact that we don't have this function
260+
// on all operation types.
261+
mlir::ArrayAttr getAsyncOnlyAttr() {
262+
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
263+
mlir::acc::KernelsOp, mlir::acc::DataOp>)
264+
return operation.getAsyncOnlyAttr();
265+
266+
// Note: 'wait' has async as well, but it cannot have data clauses, so we
267+
// don't have to handle them here.
268+
269+
llvm_unreachable("getting asyncOnly when clause not valid on operation?");
270+
}
271+
272+
// Helper function that covers for the fact that we don't have this function
273+
// on all operation types.
274+
mlir::ArrayAttr getAsyncOperandsDeviceTypeAttr() {
275+
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
276+
mlir::acc::KernelsOp, mlir::acc::DataOp>)
277+
return operation.getAsyncOperandsDeviceTypeAttr();
278+
279+
// Note: 'wait' has async as well, but it cannot have data clauses, so we
280+
// don't have to handle them here.
281+
282+
llvm_unreachable(
283+
"getting asyncOperandsDeviceType when clause not valid on operation?");
284+
}
285+
286+
// Helper function that covers for the fact that we don't have this function
287+
// on all operation types.
288+
mlir::OperandRange getAsyncOperands() {
289+
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
290+
mlir::acc::KernelsOp, mlir::acc::DataOp>)
291+
return operation.getAsyncOperands();
292+
293+
// Note: 'wait' has async as well, but it cannot have data clauses, so we
294+
// don't have to handle them here.
295+
296+
llvm_unreachable(
297+
"getting asyncOperandsDeviceType when clause not valid on operation?");
298+
}
299+
300+
// The 'data' clauses all require that we add the 'async' values from the
301+
// operation to them. We've collected the data operands along the way, so use
302+
// that list to get the current 'async' values.
303+
void updateDataOperandAsyncValues() {
304+
if (!hasAsyncClause || dataOperands.empty())
305+
return;
306+
307+
// TODO: OpenACC: Handle this correctly for combined constructs.
308+
309+
for (mlir::Operation *dataOp : dataOperands) {
310+
llvm::TypeSwitch<mlir::Operation *, void>(dataOp)
311+
.Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto op) {
312+
op.setAsyncOnlyAttr(getAsyncOnlyAttr());
313+
op.setAsyncOperandsDeviceTypeAttr(getAsyncOperandsDeviceTypeAttr());
314+
op.getAsyncOperandsMutable().assign(getAsyncOperands());
315+
})
316+
.Default([&](mlir::Operation *) {
317+
llvm_unreachable("Not a data operation?");
318+
});
319+
}
320+
}
321+
160322
public:
161323
OpenACCClauseCIREmitter(OpTy &operation, CIRGen::CIRGenFunction &cgf,
162324
CIRGen::CIRGenBuilderTy &builder,
@@ -168,6 +330,14 @@ class OpenACCClauseCIREmitter final
168330
clauseNotImplemented(clause);
169331
}
170332

333+
// The entry point for the CIR emitter. All users should use this rather than
334+
// 'visitClauseList', as this also handles the things that have to happen
335+
// 'after' the clauses are all visited.
336+
void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
337+
this->VisitClauseList(clauses);
338+
updateDataOperandAsyncValues();
339+
}
340+
171341
void VisitDefaultClause(const OpenACCDefaultClause &clause) {
172342
// This type-trait checks if 'op'(the first arg) is one of the mlir::acc
173343
// operations listed in the rest of the arguments.
@@ -250,14 +420,26 @@ class OpenACCClauseCIREmitter final
250420
}
251421

252422
void VisitAsyncClause(const OpenACCAsyncClause &clause) {
423+
hasAsyncClause = true;
253424
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
254425
mlir::acc::KernelsOp, mlir::acc::DataOp>) {
255426
if (!clause.hasIntExpr())
256427
operation.addAsyncOnly(builder.getContext(), lastDeviceTypeValues);
257-
else
258-
operation.addAsyncOperand(builder.getContext(),
259-
createIntExpr(clause.getIntExpr()),
428+
else {
429+
430+
mlir::Value intExpr;
431+
{
432+
// Async int exprs can be referenced by the data operands, which means
433+
// that the int-exprs have to appear before them. IF there is a data
434+
// operand already, set the insertion point to 'before' it.
435+
mlir::OpBuilder::InsertionGuard guardCase(builder);
436+
if (!dataOperands.empty())
437+
builder.setInsertionPoint(dataOperands.front());
438+
intExpr = createIntExpr(clause.getIntExpr());
439+
}
440+
operation.addAsyncOperand(builder.getContext(), intExpr,
260441
lastDeviceTypeValues);
442+
}
261443
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::WaitOp>) {
262444
// Wait doesn't have a device_type, so its handling here is slightly
263445
// different.
@@ -527,6 +709,20 @@ class OpenACCClauseCIREmitter final
527709
llvm_unreachable("Unknown construct kind in VisitGangClause");
528710
}
529711
}
712+
713+
void VisitCopyClause(const OpenACCCopyClause &clause) {
714+
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
715+
mlir::acc::KernelsOp>) {
716+
for (auto Var : clause.getVarList())
717+
addDataOperand<mlir::acc::CopyinOp, mlir::acc::CopyoutOp>(
718+
Var, mlir::acc::DataClause::acc_copy, /*structured=*/true,
719+
/*implicit=*/false);
720+
} else {
721+
// TODO: When we've implemented this for everything, switch this to an
722+
// unreachable. data, declare, combined constructs remain.
723+
return clauseNotImplemented(clause);
724+
}
725+
}
530726
};
531727

532728
template <typename OpTy>

clang/lib/CIR/CodeGen/CIRGenStmtOpenACC.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ mlir::LogicalResult CIRGenFunction::emitOpenACCOpAssociatedStmt(
3939
// Sets insertion point before the 'op', since every new expression needs to
4040
// be before the operation.
4141
builder.setInsertionPoint(op);
42-
makeClauseEmitter(op, *this, builder, dirKind, dirLoc)
43-
.VisitClauseList(clauses);
42+
makeClauseEmitter(op, *this, builder, dirKind, dirLoc).emitClauses(clauses);
4443
}
4544

4645
{
@@ -115,7 +114,7 @@ mlir::LogicalResult CIRGenFunction::emitOpenACCOpCombinedConstruct(
115114
// We don't bother setting the insertion point, since the clause emitter
116115
// is going to have to do this correctly.
117116
makeClauseEmitter(inf, *this, builder, dirKind, dirLoc)
118-
.VisitClauseList(clauses);
117+
.emitClauses(clauses);
119118
}
120119

121120
builder.create<TermOp>(end);
@@ -137,8 +136,7 @@ Op CIRGenFunction::emitOpenACCOp(
137136
// Sets insertion point before the 'op', since every new expression needs to
138137
// be before the operation.
139138
builder.setInsertionPoint(op);
140-
makeClauseEmitter(op, *this, builder, dirKind, dirLoc)
141-
.VisitClauseList(clauses);
139+
makeClauseEmitter(op, *this, builder, dirKind, dirLoc).emitClauses(clauses);
142140
}
143141
return op;
144142
}

clang/lib/CIR/CodeGen/CIRGenStmtOpenACCLoop.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ CIRGenFunction::emitOpenACCLoopConstruct(const OpenACCLoopConstruct &s) {
9696
builder.setInsertionPoint(op);
9797
makeClauseEmitter(op, *this, builder, s.getDirectiveKind(),
9898
s.getDirectiveLoc())
99-
.VisitClauseList(s.clauses());
99+
.emitClauses(s.clauses());
100100
}
101101

102102
mlir::LogicalResult stmtRes = mlir::success();

0 commit comments

Comments
 (0)