Skip to content

Commit b8f5cbb

Browse files
authored
[OpenACC][CIR] 'cache' construct lowering (#146915)
The 'cache' construct is an interesting one, in that it doesn't take any clauses, and is exclusively a collection of variables. Lowering wise, these just get added to the associated acc.loop. This did require some work to ensure that the cache doesn't have 'vars' that aren't inside of the loop, but Sema is taking care of that with a warning. Otherwise this is just a fairly simple amount of lowering, where each 'var' in the list creates an acc.cache, which is added to the acc.loop.
1 parent 59b1d63 commit b8f5cbb

File tree

7 files changed

+338
-118
lines changed

7 files changed

+338
-118
lines changed

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,41 @@ class CIRGenFunction : public CIRGenTypeCache {
12041204
void updateLoopOpParallelism(mlir::acc::LoopOp &op, bool isOrphan,
12051205
OpenACCDirectiveKind dk);
12061206

1207+
// The OpenACC 'cache' construct actually applies to the 'loop' if present. So
1208+
// keep track of the 'loop' so that we can add the cache vars to it correctly.
1209+
mlir::acc::LoopOp *activeLoopOp = nullptr;
1210+
1211+
struct ActiveOpenACCLoopRAII {
1212+
CIRGenFunction &cgf;
1213+
mlir::acc::LoopOp *oldLoopOp;
1214+
1215+
ActiveOpenACCLoopRAII(CIRGenFunction &cgf, mlir::acc::LoopOp *newOp)
1216+
: cgf(cgf), oldLoopOp(cgf.activeLoopOp) {
1217+
cgf.activeLoopOp = newOp;
1218+
}
1219+
~ActiveOpenACCLoopRAII() { cgf.activeLoopOp = oldLoopOp; }
1220+
};
1221+
12071222
public:
1223+
// Helper type used to store the list of important information for a 'data'
1224+
// clause variable, or a 'cache' variable reference.
1225+
struct OpenACCDataOperandInfo {
1226+
mlir::Location beginLoc;
1227+
mlir::Value varValue;
1228+
std::string name;
1229+
llvm::SmallVector<mlir::Value> bounds;
1230+
};
1231+
// Gets the collection of info required to lower and OpenACC clause or cache
1232+
// construct variable reference.
1233+
OpenACCDataOperandInfo getOpenACCDataOperandInfo(const Expr *e);
1234+
// Helper function to emit the integer expressions as required by an OpenACC
1235+
// clause/construct.
1236+
mlir::Value emitOpenACCIntExpr(const Expr *intExpr);
1237+
// Helper function to emit an integer constant as an mlir int type, used for
1238+
// constants in OpenACC constructs/clauses.
1239+
mlir::Value createOpenACCConstantInt(mlir::Location loc, unsigned width,
1240+
int64_t value);
1241+
12081242
mlir::LogicalResult
12091243
emitOpenACCComputeConstruct(const OpenACCComputeConstruct &s);
12101244
mlir::LogicalResult emitOpenACCLoopConstruct(const OpenACCLoopConstruct &s);
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Generic OpenACC lowering functions not Stmt, Decl, or clause specific.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CIRGenFunction.h"
14+
#include "mlir/Dialect/Arith/IR/Arith.h"
15+
#include "mlir/Dialect/OpenACC/OpenACC.h"
16+
#include "clang/AST/ExprCXX.h"
17+
18+
using namespace clang;
19+
using namespace clang::CIRGen;
20+
21+
namespace {
22+
mlir::Value createBound(CIRGenFunction &cgf, CIRGen::CIRGenBuilderTy &builder,
23+
mlir::Location boundLoc, mlir::Value lowerBound,
24+
mlir::Value upperBound, mlir::Value extent) {
25+
// Arrays always have a start-idx of 0.
26+
mlir::Value startIdx = cgf.createOpenACCConstantInt(boundLoc, 64, 0);
27+
// Stride is always 1 in C/C++.
28+
mlir::Value stride = cgf.createOpenACCConstantInt(boundLoc, 64, 1);
29+
30+
auto bound =
31+
builder.create<mlir::acc::DataBoundsOp>(boundLoc, lowerBound, upperBound);
32+
bound.getStartIdxMutable().assign(startIdx);
33+
if (extent)
34+
bound.getExtentMutable().assign(extent);
35+
bound.getStrideMutable().assign(stride);
36+
37+
return bound;
38+
}
39+
} // namespace
40+
41+
mlir::Value CIRGenFunction::emitOpenACCIntExpr(const Expr *intExpr) {
42+
mlir::Value expr = emitScalarExpr(intExpr);
43+
mlir::Location exprLoc = cgm.getLoc(intExpr->getBeginLoc());
44+
45+
mlir::IntegerType targetType = mlir::IntegerType::get(
46+
&getMLIRContext(), getContext().getIntWidth(intExpr->getType()),
47+
intExpr->getType()->isSignedIntegerOrEnumerationType()
48+
? mlir::IntegerType::SignednessSemantics::Signed
49+
: mlir::IntegerType::SignednessSemantics::Unsigned);
50+
51+
auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
52+
exprLoc, targetType, expr);
53+
return conversionOp.getResult(0);
54+
}
55+
56+
mlir::Value CIRGenFunction::createOpenACCConstantInt(mlir::Location loc,
57+
unsigned width,
58+
int64_t value) {
59+
mlir::IntegerType ty =
60+
mlir::IntegerType::get(&getMLIRContext(), width,
61+
mlir::IntegerType::SignednessSemantics::Signless);
62+
auto constOp = builder.create<mlir::arith::ConstantOp>(
63+
loc, builder.getIntegerAttr(ty, value));
64+
65+
return constOp.getResult();
66+
}
67+
68+
CIRGenFunction::OpenACCDataOperandInfo
69+
CIRGenFunction::getOpenACCDataOperandInfo(const Expr *e) {
70+
const Expr *curVarExpr = e->IgnoreParenImpCasts();
71+
72+
mlir::Location exprLoc = cgm.getLoc(curVarExpr->getBeginLoc());
73+
llvm::SmallVector<mlir::Value> bounds;
74+
75+
std::string exprString;
76+
llvm::raw_string_ostream os(exprString);
77+
e->printPretty(os, nullptr, getContext().getPrintingPolicy());
78+
79+
while (isa<ArraySectionExpr, ArraySubscriptExpr>(curVarExpr)) {
80+
mlir::Location boundLoc = cgm.getLoc(curVarExpr->getBeginLoc());
81+
mlir::Value lowerBound;
82+
mlir::Value upperBound;
83+
mlir::Value extent;
84+
85+
if (const auto *section = dyn_cast<ArraySectionExpr>(curVarExpr)) {
86+
if (const Expr *lb = section->getLowerBound())
87+
lowerBound = emitOpenACCIntExpr(lb);
88+
else
89+
lowerBound = createOpenACCConstantInt(boundLoc, 64, 0);
90+
91+
if (const Expr *len = section->getLength()) {
92+
extent = emitOpenACCIntExpr(len);
93+
} else {
94+
QualType baseTy = ArraySectionExpr::getBaseOriginalType(
95+
section->getBase()->IgnoreParenImpCasts());
96+
// We know this is the case as implicit lengths are only allowed for
97+
// array types with a constant size, or a dependent size. AND since
98+
// we are codegen we know we're not dependent.
99+
auto *arrayTy = getContext().getAsConstantArrayType(baseTy);
100+
// Rather than trying to calculate the extent based on the
101+
// lower-bound, we can just emit this as an upper bound.
102+
upperBound = createOpenACCConstantInt(boundLoc, 64,
103+
arrayTy->getLimitedSize() - 1);
104+
}
105+
106+
curVarExpr = section->getBase()->IgnoreParenImpCasts();
107+
} else {
108+
const auto *subscript = cast<ArraySubscriptExpr>(curVarExpr);
109+
110+
lowerBound = emitOpenACCIntExpr(subscript->getIdx());
111+
// Length of an array index is always 1.
112+
extent = createOpenACCConstantInt(boundLoc, 64, 1);
113+
curVarExpr = subscript->getBase()->IgnoreParenImpCasts();
114+
}
115+
116+
bounds.push_back(createBound(*this, this->builder, boundLoc, lowerBound,
117+
upperBound, extent));
118+
}
119+
120+
if (const auto *memExpr = dyn_cast<MemberExpr>(curVarExpr))
121+
return {exprLoc, emitMemberExpr(memExpr).getPointer(), exprString,
122+
std::move(bounds)};
123+
124+
// Sema has made sure that only 4 types of things can get here, array
125+
// subscript, array section, member expr, or DRE to a var decl (or the
126+
// former 3 wrapping a var-decl), so we should be able to assume this is
127+
// right.
128+
const auto *dre = cast<DeclRefExpr>(curVarExpr);
129+
return {exprLoc, emitDeclRefLValue(dre).getPointer(), exprString,
130+
std::move(bounds)};
131+
}

clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp

Lines changed: 6 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,7 @@ class OpenACCClauseCIREmitter final
8080
}
8181

8282
mlir::Value emitIntExpr(const Expr *intExpr) {
83-
mlir::Value expr = cgf.emitScalarExpr(intExpr);
84-
mlir::Location exprLoc = cgf.cgm.getLoc(intExpr->getBeginLoc());
85-
86-
mlir::IntegerType targetType = mlir::IntegerType::get(
87-
&cgf.getMLIRContext(), cgf.getContext().getIntWidth(intExpr->getType()),
88-
intExpr->getType()->isSignedIntegerOrEnumerationType()
89-
? mlir::IntegerType::SignednessSemantics::Signed
90-
: mlir::IntegerType::SignednessSemantics::Unsigned);
91-
92-
auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
93-
exprLoc, targetType, expr);
94-
return conversionOp.getResult(0);
83+
return cgf.emitOpenACCIntExpr(intExpr);
9584
}
9685

9786
// 'condition' as an OpenACC grammar production is used for 'if' and (some
@@ -111,6 +100,7 @@ class OpenACCClauseCIREmitter final
111100

112101
mlir::Value createConstantInt(mlir::Location loc, unsigned width,
113102
int64_t value) {
103+
return cgf.createOpenACCConstantInt(loc, width, value);
114104
mlir::IntegerType ty = mlir::IntegerType::get(
115105
&cgf.getMLIRContext(), width,
116106
mlir::IntegerType::SignednessSemantics::Signless);
@@ -184,105 +174,6 @@ class OpenACCClauseCIREmitter final
184174
dataOperands.append(computeEmitter.dataOperands);
185175
}
186176

187-
struct DataOperandInfo {
188-
mlir::Location beginLoc;
189-
mlir::Value varValue;
190-
std::string name;
191-
llvm::SmallVector<mlir::Value> bounds;
192-
};
193-
194-
mlir::Value createBound(mlir::Location boundLoc, mlir::Value lowerBound,
195-
mlir::Value upperBound, mlir::Value extent) {
196-
// Arrays always have a start-idx of 0.
197-
mlir::Value startIdx = createConstantInt(boundLoc, 64, 0);
198-
// Stride is always 1 in C/C++.
199-
mlir::Value stride = createConstantInt(boundLoc, 64, 1);
200-
201-
auto bound = builder.create<mlir::acc::DataBoundsOp>(boundLoc, lowerBound,
202-
upperBound);
203-
bound.getStartIdxMutable().assign(startIdx);
204-
if (extent)
205-
bound.getExtentMutable().assign(extent);
206-
bound.getStrideMutable().assign(stride);
207-
208-
return bound;
209-
}
210-
211-
// A helper function that gets the information from an operand to a data
212-
// clause, so that it can be used to emit the data operations.
213-
DataOperandInfo getDataOperandInfo(OpenACCDirectiveKind dk, const Expr *e) {
214-
// TODO: OpenACC: Cache was different enough as to need a separate
215-
// `ActOnCacheVar`, so we are going to need to do some investigations here
216-
// when it comes to implement this for cache.
217-
if (dk == OpenACCDirectiveKind::Cache) {
218-
cgf.cgm.errorNYI(e->getSourceRange(),
219-
"OpenACC data operand for 'cache' directive");
220-
return {cgf.cgm.getLoc(e->getBeginLoc()), {}, {}, {}};
221-
}
222-
223-
const Expr *curVarExpr = e->IgnoreParenImpCasts();
224-
225-
mlir::Location exprLoc = cgf.cgm.getLoc(curVarExpr->getBeginLoc());
226-
llvm::SmallVector<mlir::Value> bounds;
227-
228-
std::string exprString;
229-
llvm::raw_string_ostream os(exprString);
230-
e->printPretty(os, nullptr, cgf.getContext().getPrintingPolicy());
231-
232-
// Assemble the list of bounds.
233-
while (isa<ArraySectionExpr, ArraySubscriptExpr>(curVarExpr)) {
234-
mlir::Location boundLoc = cgf.cgm.getLoc(curVarExpr->getBeginLoc());
235-
mlir::Value lowerBound;
236-
mlir::Value upperBound;
237-
mlir::Value extent;
238-
239-
if (const auto *section = dyn_cast<ArraySectionExpr>(curVarExpr)) {
240-
if (const Expr *lb = section->getLowerBound())
241-
lowerBound = emitIntExpr(lb);
242-
else
243-
lowerBound = createConstantInt(boundLoc, 64, 0);
244-
245-
if (const Expr *len = section->getLength()) {
246-
extent = emitIntExpr(len);
247-
} else {
248-
QualType baseTy = ArraySectionExpr::getBaseOriginalType(
249-
section->getBase()->IgnoreParenImpCasts());
250-
// We know this is the case as implicit lengths are only allowed for
251-
// array types with a constant size, or a dependent size. AND since
252-
// we are codegen we know we're not dependent.
253-
auto *arrayTy = cgf.getContext().getAsConstantArrayType(baseTy);
254-
// Rather than trying to calculate the extent based on the
255-
// lower-bound, we can just emit this as an upper bound.
256-
upperBound =
257-
createConstantInt(boundLoc, 64, arrayTy->getLimitedSize() - 1);
258-
}
259-
260-
curVarExpr = section->getBase()->IgnoreParenImpCasts();
261-
} else {
262-
const auto *subscript = cast<ArraySubscriptExpr>(curVarExpr);
263-
264-
lowerBound = emitIntExpr(subscript->getIdx());
265-
// Length of an array index is always 1.
266-
extent = createConstantInt(boundLoc, 64, 1);
267-
curVarExpr = subscript->getBase()->IgnoreParenImpCasts();
268-
}
269-
270-
bounds.push_back(createBound(boundLoc, lowerBound, upperBound, extent));
271-
}
272-
273-
if (const auto *memExpr = dyn_cast<MemberExpr>(curVarExpr))
274-
return {exprLoc, cgf.emitMemberExpr(memExpr).getPointer(), exprString,
275-
std::move(bounds)};
276-
277-
// Sema has made sure that only 4 types of things can get here, array
278-
// subscript, array section, member expr, or DRE to a var decl (or the
279-
// former 3 wrapping a var-decl), so we should be able to assume this is
280-
// right.
281-
const auto *dre = cast<DeclRefExpr>(curVarExpr);
282-
return {exprLoc, cgf.emitDeclRefLValue(dre).getPointer(), exprString,
283-
std::move(bounds)};
284-
}
285-
286177
mlir::acc::DataClauseModifier
287178
convertModifiers(OpenACCModifierKind modifiers) {
288179
using namespace mlir::acc;
@@ -314,7 +205,8 @@ class OpenACCClauseCIREmitter final
314205
void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,
315206
OpenACCModifierKind modifiers, bool structured,
316207
bool implicit) {
317-
DataOperandInfo opInfo = getDataOperandInfo(dirKind, varOperand);
208+
CIRGenFunction::OpenACCDataOperandInfo opInfo =
209+
cgf.getOpenACCDataOperandInfo(varOperand);
318210

319211
auto beforeOp =
320212
builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,
@@ -355,7 +247,8 @@ class OpenACCClauseCIREmitter final
355247
void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,
356248
OpenACCModifierKind modifiers, bool structured,
357249
bool implicit) {
358-
DataOperandInfo opInfo = getDataOperandInfo(dirKind, varOperand);
250+
CIRGenFunction::OpenACCDataOperandInfo opInfo =
251+
cgf.getOpenACCDataOperandInfo(varOperand);
359252
auto beforeOp =
360253
builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,
361254
implicit, opInfo.name, opInfo.bounds);

clang/lib/CIR/CodeGen/CIRGenStmtOpenACC.cpp

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ mlir::LogicalResult CIRGenFunction::emitOpenACCOpCombinedConstruct(
9595
builder.setInsertionPointToEnd(&innerBlock);
9696

9797
LexicalScope ls{*this, start, builder.getInsertionBlock()};
98+
ActiveOpenACCLoopRAII activeLoop{*this, &loopOp};
99+
98100
res = emitStmt(loopStmt, /*useCurrentScope=*/true);
99101

100102
builder.create<mlir::acc::YieldOp>(end);
@@ -271,13 +273,39 @@ CIRGenFunction::emitOpenACCUpdateConstruct(const OpenACCUpdateConstruct &s) {
271273
s.clauses());
272274
return mlir::success();
273275
}
276+
274277
mlir::LogicalResult
275-
CIRGenFunction::emitOpenACCAtomicConstruct(const OpenACCAtomicConstruct &s) {
276-
cgm.errorNYI(s.getSourceRange(), "OpenACC Atomic Construct");
277-
return mlir::failure();
278+
CIRGenFunction::emitOpenACCCacheConstruct(const OpenACCCacheConstruct &s) {
279+
// The 'cache' directive 'may' be at the top of a loop by standard, but
280+
// doesn't have to be. Additionally, there is nothing that requires this be a
281+
// loop affected by an OpenACC pragma. Sema doesn't do any level of
282+
// enforcement here, since it isn't particularly valuable to do so thanks to
283+
// that. Instead, we treat cache as a 'noop' if there is no acc.loop to apply
284+
// it to.
285+
if (!activeLoopOp)
286+
return mlir::success();
287+
288+
mlir::acc::LoopOp loopOp = *activeLoopOp;
289+
290+
mlir::OpBuilder::InsertionGuard guard(builder);
291+
builder.setInsertionPoint(loopOp);
292+
293+
for (const Expr *var : s.getVarList()) {
294+
CIRGenFunction::OpenACCDataOperandInfo opInfo =
295+
getOpenACCDataOperandInfo(var);
296+
297+
auto cacheOp = builder.create<CacheOp>(
298+
opInfo.beginLoc, opInfo.varValue,
299+
/*structured=*/false, /*implicit=*/false, opInfo.name, opInfo.bounds);
300+
301+
loopOp.getCacheOperandsMutable().append(cacheOp.getResult());
302+
}
303+
304+
return mlir::success();
278305
}
306+
279307
mlir::LogicalResult
280-
CIRGenFunction::emitOpenACCCacheConstruct(const OpenACCCacheConstruct &s) {
281-
cgm.errorNYI(s.getSourceRange(), "OpenACC Cache Construct");
308+
CIRGenFunction::emitOpenACCAtomicConstruct(const OpenACCAtomicConstruct &s) {
309+
cgm.errorNYI(s.getSourceRange(), "OpenACC Atomic Construct");
282310
return mlir::failure();
283311
}

clang/lib/CIR/CodeGen/CIRGenStmtOpenACCLoop.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ CIRGenFunction::emitOpenACCLoopConstruct(const OpenACCLoopConstruct &s) {
130130
mlir::OpBuilder::InsertionGuard guardCase(builder);
131131
builder.setInsertionPointToEnd(&block);
132132
LexicalScope ls{*this, start, builder.getInsertionBlock()};
133+
ActiveOpenACCLoopRAII activeLoop{*this, &op};
133134

134135
stmtRes = emitStmt(s.getLoop(), /*useCurrentScope=*/true);
135136
builder.create<mlir::acc::YieldOp>(end);

clang/lib/CIR/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_clang_library(clangCIR
2525
CIRGenFunction.cpp
2626
CIRGenItaniumCXXABI.cpp
2727
CIRGenModule.cpp
28+
CIRGenOpenACC.cpp
2829
CIRGenOpenACCClause.cpp
2930
CIRGenRecordLayoutBuilder.cpp
3031
CIRGenStmt.cpp

0 commit comments

Comments
 (0)