Skip to content

Commit b307347

Browse files
authored
[OpenACC][CIR] Lowering for atomic-read (#164299)
The OpenACC spec allows only `v = x` form for atomic-read, and only when both are L-values. The result is this ends up being a pretty trivial patch, however it adds a decent amount of infrastructure for the other forms of atomic. Additionally, the 3.4 spec starts allowing the 'if' clause on atomic, which has recently been added to the ACC dialect. This patch also ensures that can be lowered as well. Extensive testing of this feature was done on other clauses, so there isn't much further work/testing to be done for it.
1 parent aca53f4 commit b307347

File tree

5 files changed

+107
-8
lines changed

5 files changed

+107
-8
lines changed

clang/include/clang/AST/StmtOpenACC.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,17 @@ class OpenACCAtomicConstruct final
815815
Stmt *getAssociatedStmt() {
816816
return OpenACCAssociatedStmtConstruct::getAssociatedStmt();
817817
}
818+
819+
// A struct to represent a broken-down version of the associated statement,
820+
// providing the information specified in OpenACC3.3 Section 2.12.
821+
struct StmtInfo {
822+
const Expr *V;
823+
const Expr *X;
824+
// TODO: OpenACC: We should expand this as we're implementing the other
825+
// atomic construct kinds.
826+
};
827+
828+
const StmtInfo getAssociatedStmtInfo() const;
818829
};
819830

820831
} // namespace clang

clang/lib/AST/StmtOpenACC.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
#include "clang/AST/StmtOpenACC.h"
1414
#include "clang/AST/ASTContext.h"
15+
#include "clang/AST/ExprCXX.h"
1516
#include "clang/AST/StmtCXX.h"
17+
1618
using namespace clang;
1719

1820
OpenACCComputeConstruct *
@@ -322,6 +324,38 @@ OpenACCAtomicConstruct *OpenACCAtomicConstruct::Create(
322324
return Inst;
323325
}
324326

327+
const OpenACCAtomicConstruct::StmtInfo
328+
OpenACCAtomicConstruct::getAssociatedStmtInfo() const {
329+
// This ends up being a vastly simplified version of SemaOpenACCAtomic, since
330+
// it doesn't have to worry about erroring out, but we should do a lot of
331+
// asserts to ensure we don't get off into the weeds.
332+
assert(getAssociatedStmt() && "invalid associated stmt?");
333+
334+
switch (AtomicKind) {
335+
case OpenACCAtomicKind::None:
336+
case OpenACCAtomicKind::Write:
337+
case OpenACCAtomicKind::Update:
338+
case OpenACCAtomicKind::Capture:
339+
assert(false && "Only 'read' has been implemented here");
340+
return {};
341+
case OpenACCAtomicKind::Read: {
342+
// Read only supports the format 'v = x'; where both sides are a scalar
343+
// expression. This can come in 2 forms; BinaryOperator or
344+
// CXXOperatorCallExpr (rarely).
345+
const Expr *AssignExpr = cast<const Expr>(getAssociatedStmt());
346+
if (const auto *BO = dyn_cast<BinaryOperator>(AssignExpr)) {
347+
assert(BO->getOpcode() == BO_Assign);
348+
return {BO->getLHS()->IgnoreImpCasts(), BO->getRHS()->IgnoreImpCasts()};
349+
}
350+
351+
const auto *OO = cast<CXXOperatorCallExpr>(AssignExpr);
352+
assert(OO->getOperator() == OO_Equal);
353+
354+
return {OO->getArg(0)->IgnoreImpCasts(), OO->getArg(1)->IgnoreImpCasts()};
355+
}
356+
}
357+
}
358+
325359
OpenACCCacheConstruct *OpenACCCacheConstruct::CreateEmpty(const ASTContext &C,
326360
unsigned NumVars) {
327361
void *Mem =

clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -553,12 +553,15 @@ class OpenACCClauseCIREmitter final
553553
}
554554

555555
void VisitIfClause(const OpenACCIfClause &clause) {
556-
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
557-
mlir::acc::KernelsOp, mlir::acc::InitOp,
558-
mlir::acc::ShutdownOp, mlir::acc::SetOp,
559-
mlir::acc::DataOp, mlir::acc::WaitOp,
560-
mlir::acc::HostDataOp, mlir::acc::EnterDataOp,
561-
mlir::acc::ExitDataOp, mlir::acc::UpdateOp>) {
556+
if constexpr (isOneOfTypes<
557+
OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
558+
mlir::acc::KernelsOp, mlir::acc::InitOp,
559+
mlir::acc::ShutdownOp, mlir::acc::SetOp,
560+
mlir::acc::DataOp, mlir::acc::WaitOp,
561+
mlir::acc::HostDataOp, mlir::acc::EnterDataOp,
562+
mlir::acc::ExitDataOp, mlir::acc::UpdateOp,
563+
mlir::acc::AtomicReadOp, mlir::acc::AtomicWriteOp,
564+
mlir::acc::AtomicUpdateOp, mlir::acc::AtomicCaptureOp>) {
562565
operation.getIfCondMutable().append(
563566
createCondition(clause.getConditionExpr()));
564567
} else if constexpr (isCombinedType<OpTy>) {
@@ -1144,6 +1147,10 @@ EXPL_SPEC(mlir::acc::HostDataOp)
11441147
EXPL_SPEC(mlir::acc::EnterDataOp)
11451148
EXPL_SPEC(mlir::acc::ExitDataOp)
11461149
EXPL_SPEC(mlir::acc::UpdateOp)
1150+
EXPL_SPEC(mlir::acc::AtomicReadOp)
1151+
EXPL_SPEC(mlir::acc::AtomicWriteOp)
1152+
EXPL_SPEC(mlir::acc::AtomicCaptureOp)
1153+
EXPL_SPEC(mlir::acc::AtomicUpdateOp)
11471154
#undef EXPL_SPEC
11481155

11491156
template <typename ComputeOp, typename LoopOp>

clang/lib/CIR/CodeGen/CIRGenStmtOpenACC.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,29 @@ CIRGenFunction::emitOpenACCCacheConstruct(const OpenACCCacheConstruct &s) {
306306

307307
mlir::LogicalResult
308308
CIRGenFunction::emitOpenACCAtomicConstruct(const OpenACCAtomicConstruct &s) {
309-
cgm.errorNYI(s.getSourceRange(), "OpenACC Atomic Construct");
310-
return mlir::failure();
309+
// For now, we are only support 'read', so diagnose. We can switch on the kind
310+
// later once we start implementing the other 3 forms.
311+
if (s.getAtomicKind() != OpenACCAtomicKind::Read) {
312+
cgm.errorNYI(s.getSourceRange(), "OpenACC Atomic Construct");
313+
return mlir::failure();
314+
}
315+
316+
// While Atomic is an 'associated statement' construct, it 'steals' the
317+
// expression it is associated with rather than emitting it inside of it. So
318+
// it has custom emit logic.
319+
mlir::Location start = getLoc(s.getSourceRange().getBegin());
320+
OpenACCAtomicConstruct::StmtInfo inf = s.getAssociatedStmtInfo();
321+
// Atomic 'read' only permits 'v = x', where v and x are both scalar L values.
322+
// The getAssociatedStmtInfo strips off implicit casts, which includes
323+
// implicit conversions and L-to-R-Value conversions, so we can just emit it
324+
// as an L value. The Flang implementation has no problem with different
325+
// types, so it appears that the dialect can handle the conversions.
326+
mlir::Value v = emitLValue(inf.V).getPointer();
327+
mlir::Value x = emitLValue(inf.X).getPointer();
328+
mlir::Type resTy = convertType(inf.V->getType());
329+
auto op = mlir::acc::AtomicReadOp::create(builder, start, x, v, resTy,
330+
/*ifCond=*/{});
331+
emitOpenACCClauses(op, s.getDirectiveKind(), s.getDirectiveLoc(),
332+
s.clauses());
333+
return mlir::success();
311334
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_cc1 -fopenacc -triple x86_64-linux-gnu -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir -triple x86_64-linux-pc %s -o - | FileCheck %s
2+
3+
void use(int x, unsigned int y, float f) {
4+
// CHECK: cir.func{{.*}}(%[[X_ARG:.*]]: !s32i{{.*}}, %[[Y_ARG:.*]]: !u32i{{.*}}, %[[F_ARG:.*]]: !cir.float{{.*}}){{.*}}{
5+
// CHECK-NEXT: %[[X_ALLOC:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
6+
// CHECK-NEXT: %[[Y_ALLOC:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["y", init]
7+
// CHECK-NEXT: %[[F_ALLOC:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f", init]
8+
// CHECK-NEXT: cir.store %[[X_ARG]], %[[X_ALLOC]] : !s32i, !cir.ptr<!s32i>
9+
// CHECK-NEXT: cir.store %[[Y_ARG]], %[[Y_ALLOC]] : !u32i, !cir.ptr<!u32i>
10+
// CHECK-NEXT: cir.store %[[F_ARG]], %[[F_ALLOC]] : !cir.float, !cir.ptr<!cir.float>
11+
12+
// CHECK-NEXT: acc.atomic.read %[[X_ALLOC]] = %[[Y_ALLOC]] : !cir.ptr<!s32i>, !cir.ptr<!u32i>, !s32i
13+
#pragma acc atomic read
14+
x = y;
15+
16+
// CHECK-NEXT: %[[X_LOAD:.*]] = cir.load{{.*}} %[[X_ALLOC]] : !cir.ptr<!s32i>, !s32i
17+
// CHECK-NEXT: %[[X_CAST:.*]] = cir.cast integral %[[X_LOAD]] : !s32i -> !u32i
18+
// CHECK-NEXT: %[[Y_LOAD:.*]] = cir.load{{.*}} %[[Y_ALLOC]] : !cir.ptr<!u32i>, !u32i
19+
// CHECK-NEXT: %[[CMP:.*]] = cir.cmp(eq, %[[X_CAST]], %[[Y_LOAD]]) : !u32i, !cir.bool
20+
// CHECK-NEXT: %[[CMP_CAST:.*]] = builtin.unrealized_conversion_cast %[[CMP]] : !cir.bool to i1
21+
// CHECK-NEXT: acc.atomic.read if(%[[CMP_CAST]]) %[[F_ALLOC]] = %[[Y_ALLOC]] : !cir.ptr<!cir.float>, !cir.ptr<!u32i>, !cir.float
22+
#pragma acc atomic read if (x == y)
23+
f = y;
24+
}

0 commit comments

Comments
 (0)