Skip to content

[OpenACC][CIR] Implement 'private' clause lowering. #151360

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 2 commits into from
Aug 1, 2025
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
6 changes: 4 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ using namespace clang;
using namespace clang::CIRGen;

CIRGenFunction::AutoVarEmission
CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
CIRGenFunction::emitAutoVarAlloca(const VarDecl &d,
mlir::OpBuilder::InsertPoint ip) {
QualType ty = d.getType();
if (ty.getAddressSpace() != LangAS::Default)
cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: address space");
Expand All @@ -50,7 +51,8 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
// A normal fixed sized variable becomes an alloca in the entry block,
mlir::Type allocaTy = convertTypeForMem(ty);
// Create the temp alloca and declare variable using it.
address = createTempAlloca(allocaTy, alignment, loc, d.getName());
address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
/*arraySize=*/nullptr, /*alloca=*/nullptr, ip);
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);

emission.Addr = address;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,8 @@ class CIRGenFunction : public CIRGenTypeCache {

Address emitArrayToPointerDecay(const Expr *array);

AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d);
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d,
mlir::OpBuilder::InsertPoint ip = {});

/// Emit code and set up symbol table for a variable declaration with auto,
/// register, or no storage class specifier. These turn into simple stack
Expand Down Expand Up @@ -1375,6 +1376,7 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::Location beginLoc;
mlir::Value varValue;
std::string name;
QualType baseType;
llvm::SmallVector<mlir::Value> bounds;
};
// Gets the collection of info required to lower and OpenACC clause or cache
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@ CIRGenFunction::getOpenACCDataOperandInfo(const Expr *e) {

if (const auto *memExpr = dyn_cast<MemberExpr>(curVarExpr))
return {exprLoc, emitMemberExpr(memExpr).getPointer(), exprString,
std::move(bounds)};
curVarExpr->getType(), std::move(bounds)};

// Sema has made sure that only 4 types of things can get here, array
// subscript, array section, member expr, or DRE to a var decl (or the
// former 3 wrapping a var-decl), so we should be able to assume this is
// right.
const auto *dre = cast<DeclRefExpr>(curVarExpr);
return {exprLoc, emitDeclRefLValue(dre).getPointer(), exprString,
std::move(bounds)};
curVarExpr->getType(), std::move(bounds)};
}
136 changes: 136 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <type_traits>

#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"

#include "clang/AST/ExprCXX.h"
Expand Down Expand Up @@ -355,6 +356,110 @@ class OpenACCClauseCIREmitter final
}
}

template <typename RecipeTy>
RecipeTy getOrCreateRecipe(ASTContext &astCtx, const Expr *varRef,
DeclContext *dc, QualType baseType,
mlir::Value mainOp) {
mlir::ModuleOp mod =
builder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
builder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
builder.getBlock()->getParentOfType<mlir::ModuleOp>();

getParentOfType walks the hierarchy until it finds the requested type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

mlir::Block doesn't seem to have getParentOfType? I think that must be why this uses getParent. OR am I missing something further?


std::string recipeName;
{
llvm::raw_string_ostream stream(recipeName);
if constexpr (std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
stream << "privatization_";
} else if constexpr (std::is_same_v<RecipeTy,
mlir::acc::FirstprivateRecipeOp>) {
stream << "firstprivatization_";

} else if constexpr (std::is_same_v<RecipeTy,
mlir::acc::ReductionRecipeOp>) {
stream << "reduction_";
// We don't have the reduction operation here well enough to know how to
// spell this correctly (+ == 'add', etc), so when we implement
// 'reduction' we have to do that here.
cgf.cgm.errorNYI(varRef->getSourceRange(),
"OpeNACC reduction recipe creation");
} else {
static_assert(!sizeof(RecipeTy), "Unknown Recipe op kind");
}

MangleContext &mc = cgf.cgm.getCXXABI().getMangleContext();
mc.mangleCanonicalTypeName(baseType, stream);
}

if (auto recipe = mod.lookupSymbol<RecipeTy>(recipeName))
return recipe;

mlir::Location loc = cgf.cgm.getLoc(varRef->getBeginLoc());
mlir::Location locEnd = cgf.cgm.getLoc(varRef->getEndLoc());

mlir::OpBuilder modBuilder(mod.getBodyRegion());
auto recipe =
RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType());

// Magic-up a var-decl so we can use normal init/destruction operations for
// a variable declaration.
VarDecl &tempDecl = *VarDecl::Create(
astCtx, dc, varRef->getBeginLoc(), varRef->getBeginLoc(),
&astCtx.Idents.get("openacc.private.init"), baseType,
astCtx.getTrivialTypeSourceInfo(baseType), SC_Auto);
CIRGenFunction::AutoVarEmission tempDeclEmission{
CIRGenFunction::AutoVarEmission::invalid()};

// Init section.
{
llvm::SmallVector<mlir::Type> argsTys{mainOp.getType()};
llvm::SmallVector<mlir::Location> argsLocs{loc};
builder.createBlock(&recipe.getInitRegion(), recipe.getInitRegion().end(),
argsTys, argsLocs);
builder.setInsertionPointToEnd(&recipe.getInitRegion().back());

if constexpr (!std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
// We have only implemented 'init' for private, so make this NYI until
// we have explicitly implemented everything.
cgf.cgm.errorNYI(varRef->getSourceRange(),
"OpenACC non-private recipe init");
}

tempDeclEmission =
cgf.emitAutoVarAlloca(tempDecl, builder.saveInsertionPoint());
cgf.emitAutoVarInit(tempDeclEmission);

mlir::acc::YieldOp::create(builder, locEnd);
}

// Copy section.
if constexpr (std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp> ||
std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
// TODO: OpenACC: 'private' doesn't emit this, but for the other two we
// have to figure out what 'copy' means here.
cgf.cgm.errorNYI(varRef->getSourceRange(),
"OpenACC record type privatization copy section");
}

// Destroy section (doesn't currently exist).
if (tempDecl.needsDestruction(cgf.getContext())) {
llvm::SmallVector<mlir::Type> argsTys{mainOp.getType()};
llvm::SmallVector<mlir::Location> argsLocs{loc};
mlir::Block *block = builder.createBlock(&recipe.getDestroyRegion(),
recipe.getDestroyRegion().end(),
argsTys, argsLocs);
builder.setInsertionPointToEnd(&recipe.getDestroyRegion().back());

mlir::Type elementTy =
mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
Address addr{block->getArgument(0), elementTy,
cgf.getContext().getDeclAlign(&tempDecl)};
cgf.emitDestroy(addr, baseType,
cgf.getDestroyer(QualType::DK_cxx_destructor));

mlir::acc::YieldOp::create(builder, locEnd);
}

return recipe;
}

public:
OpenACCClauseCIREmitter(OpTy &operation, CIRGen::CIRGenFunction &cgf,
CIRGen::CIRGenBuilderTy &builder,
Expand Down Expand Up @@ -971,6 +1076,37 @@ class OpenACCClauseCIREmitter final
llvm_unreachable("Unknown construct kind in VisitAttachClause");
}
}

void VisitPrivateClause(const OpenACCPrivateClause &clause) {
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
mlir::acc::LoopOp>) {
for (const Expr *var : clause.getVarList()) {
CIRGenFunction::OpenACCDataOperandInfo opInfo =
cgf.getOpenACCDataOperandInfo(var);
auto privateOp = mlir::acc::PrivateOp::create(
builder, opInfo.beginLoc, opInfo.varValue, /*structured=*/true,
/*implicit=*/false, opInfo.name, opInfo.bounds);
privateOp.setDataClause(mlir::acc::DataClause::acc_private);

{
mlir::OpBuilder::InsertionGuard guardCase(builder);
auto recipe = getOrCreateRecipe<mlir::acc::PrivateRecipeOp>(
cgf.getContext(), var, Decl::castToDeclContext(cgf.curFuncDecl),
opInfo.baseType, privateOp.getResult());
// TODO: OpenACC: The dialect is going to change in the near future to
// have these be on a different operation, so when that changes, we
// probably need to change these here.
operation.addPrivatization(builder.getContext(), privateOp, recipe);
}
}
} else if constexpr (isCombinedType<OpTy>) {
// Despite this being valid on ParallelOp or SerialOp, combined type
// applies to the 'loop'.
applyToLoopOp(clause);
} else {
llvm_unreachable("Unknown construct kind in VisitPrivateClause");
}
}
};

template <typename OpTy>
Expand Down
Loading