Path: blob/main/contrib/llvm-project/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
213799 views
//===----------------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Emit OpenACC clause nodes as CIR code.9//10//===----------------------------------------------------------------------===//1112#include <type_traits>1314#include "CIRGenFunction.h"1516#include "clang/AST/ExprCXX.h"1718#include "mlir/Dialect/Arith/IR/Arith.h"19#include "mlir/Dialect/OpenACC/OpenACC.h"20#include "llvm/ADT/TypeSwitch.h"2122using namespace clang;23using namespace clang::CIRGen;2425namespace {26// Simple type-trait to see if the first template arg is one of the list, so we27// can tell whether to `if-constexpr` a bunch of stuff.28template <typename ToTest, typename T, typename... Tys>29constexpr bool isOneOfTypes =30std::is_same_v<ToTest, T> || isOneOfTypes<ToTest, Tys...>;31template <typename ToTest, typename T>32constexpr bool isOneOfTypes<ToTest, T> = std::is_same_v<ToTest, T>;3334// Holds information for emitting clauses for a combined construct. We35// instantiate the clause emitter with this type so that it can use36// if-constexpr to specially handle these.37template <typename CompOpTy> struct CombinedConstructClauseInfo {38using ComputeOpTy = CompOpTy;39ComputeOpTy computeOp;40mlir::acc::LoopOp loopOp;41};42template <typename ToTest> constexpr bool isCombinedType = false;43template <typename T>44constexpr bool isCombinedType<CombinedConstructClauseInfo<T>> = true;4546template <typename OpTy>47class OpenACCClauseCIREmitter final48: public OpenACCClauseVisitor<OpenACCClauseCIREmitter<OpTy>> {49// Necessary for combined constructs.50template <typename FriendOpTy> friend class OpenACCClauseCIREmitter;5152OpTy &operation;53CIRGen::CIRGenFunction &cgf;54CIRGen::CIRGenBuilderTy &builder;5556// This is necessary since a few of the clauses emit differently based on the57// directive kind they are attached to.58OpenACCDirectiveKind dirKind;59// TODO(cir): This source location should be able to go away once the NYI60// diagnostics are gone.61SourceLocation dirLoc;6263llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;64// Keep track of the async-clause so that we can shortcut updating the data65// operands async clauses.66bool hasAsyncClause = false;67// Keep track of the data operands so that we can update their async clauses.68llvm::SmallVector<mlir::Operation *> dataOperands;6970void clauseNotImplemented(const OpenACCClause &c) {71cgf.cgm.errorNYI(c.getSourceRange(), "OpenACC Clause", c.getClauseKind());72}7374void setLastDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {75lastDeviceTypeValues.clear();7677for (const DeviceTypeArgument &arg : clause.getArchitectures())78lastDeviceTypeValues.push_back(decodeDeviceType(arg.getIdentifierInfo()));79}8081mlir::Value emitIntExpr(const Expr *intExpr) {82return cgf.emitOpenACCIntExpr(intExpr);83}8485// 'condition' as an OpenACC grammar production is used for 'if' and (some86// variants of) 'self'. It needs to be emitted as a signless-1-bit value, so87// this function emits the expression, then sets the unrealized conversion88// cast correctly, and returns the completed value.89mlir::Value createCondition(const Expr *condExpr) {90mlir::Value condition = cgf.evaluateExprAsBool(condExpr);91mlir::Location exprLoc = cgf.cgm.getLoc(condExpr->getBeginLoc());92mlir::IntegerType targetType = mlir::IntegerType::get(93&cgf.getMLIRContext(), /*width=*/1,94mlir::IntegerType::SignednessSemantics::Signless);95auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(96exprLoc, targetType, condition);97return conversionOp.getResult(0);98}99100mlir::Value createConstantInt(mlir::Location loc, unsigned width,101int64_t value) {102return cgf.createOpenACCConstantInt(loc, width, value);103mlir::IntegerType ty = mlir::IntegerType::get(104&cgf.getMLIRContext(), width,105mlir::IntegerType::SignednessSemantics::Signless);106auto constOp = builder.create<mlir::arith::ConstantOp>(107loc, builder.getIntegerAttr(ty, value));108109return constOp.getResult();110}111112mlir::Value createConstantInt(SourceLocation loc, unsigned width,113int64_t value) {114return createConstantInt(cgf.cgm.getLoc(loc), width, value);115}116117mlir::acc::DeviceType decodeDeviceType(const IdentifierInfo *ii) {118// '*' case leaves no identifier-info, just a nullptr.119if (!ii)120return mlir::acc::DeviceType::Star;121return llvm::StringSwitch<mlir::acc::DeviceType>(ii->getName())122.CaseLower("default", mlir::acc::DeviceType::Default)123.CaseLower("host", mlir::acc::DeviceType::Host)124.CaseLower("multicore", mlir::acc::DeviceType::Multicore)125.CasesLower("nvidia", "acc_device_nvidia",126mlir::acc::DeviceType::Nvidia)127.CaseLower("radeon", mlir::acc::DeviceType::Radeon);128}129130mlir::acc::GangArgType decodeGangType(OpenACCGangKind gk) {131switch (gk) {132case OpenACCGangKind::Num:133return mlir::acc::GangArgType::Num;134case OpenACCGangKind::Dim:135return mlir::acc::GangArgType::Dim;136case OpenACCGangKind::Static:137return mlir::acc::GangArgType::Static;138}139llvm_unreachable("unknown gang kind");140}141142template <typename U = void,143typename = std::enable_if_t<isCombinedType<OpTy>, U>>144void applyToLoopOp(const OpenACCClause &c) {145mlir::OpBuilder::InsertionGuard guardCase(builder);146builder.setInsertionPoint(operation.loopOp);147OpenACCClauseCIREmitter<mlir::acc::LoopOp> loopEmitter{148operation.loopOp, cgf, builder, dirKind, dirLoc};149loopEmitter.lastDeviceTypeValues = lastDeviceTypeValues;150loopEmitter.Visit(&c);151}152153template <typename U = void,154typename = std::enable_if_t<isCombinedType<OpTy>, U>>155void applyToComputeOp(const OpenACCClause &c) {156mlir::OpBuilder::InsertionGuard guardCase(builder);157builder.setInsertionPoint(operation.computeOp);158OpenACCClauseCIREmitter<typename OpTy::ComputeOpTy> computeEmitter{159operation.computeOp, cgf, builder, dirKind, dirLoc};160161computeEmitter.lastDeviceTypeValues = lastDeviceTypeValues;162163// Async handler uses the first data operand to figure out where to insert164// its information if it is present. This ensures that the new handler will165// correctly set the insertion point for async.166if (!dataOperands.empty())167computeEmitter.dataOperands.push_back(dataOperands.front());168computeEmitter.Visit(&c);169170// Make sure all of the new data operands are kept track of here. The171// combined constructs always apply 'async' to only the compute component,172// so we need to collect these.173dataOperands.append(computeEmitter.dataOperands);174}175176mlir::acc::DataClauseModifier177convertModifiers(OpenACCModifierKind modifiers) {178using namespace mlir::acc;179static_assert(static_cast<int>(OpenACCModifierKind::Zero) ==180static_cast<int>(DataClauseModifier::zero) &&181static_cast<int>(OpenACCModifierKind::Readonly) ==182static_cast<int>(DataClauseModifier::readonly) &&183static_cast<int>(OpenACCModifierKind::AlwaysIn) ==184static_cast<int>(DataClauseModifier::alwaysin) &&185static_cast<int>(OpenACCModifierKind::AlwaysOut) ==186static_cast<int>(DataClauseModifier::alwaysout) &&187static_cast<int>(OpenACCModifierKind::Capture) ==188static_cast<int>(DataClauseModifier::capture));189190DataClauseModifier mlirModifiers{};191192// The MLIR representation of this represents `always` as `alwaysin` +193// `alwaysout`. So do a small fixup here.194if (isOpenACCModifierBitSet(modifiers, OpenACCModifierKind::Always)) {195mlirModifiers = mlirModifiers | DataClauseModifier::always;196modifiers &= ~OpenACCModifierKind::Always;197}198199mlirModifiers = mlirModifiers | static_cast<DataClauseModifier>(modifiers);200return mlirModifiers;201}202203template <typename BeforeOpTy, typename AfterOpTy>204void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,205OpenACCModifierKind modifiers, bool structured,206bool implicit) {207CIRGenFunction::OpenACCDataOperandInfo opInfo =208cgf.getOpenACCDataOperandInfo(varOperand);209210auto beforeOp =211builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,212implicit, opInfo.name, opInfo.bounds);213operation.getDataClauseOperandsMutable().append(beforeOp.getResult());214215AfterOpTy afterOp;216{217mlir::OpBuilder::InsertionGuard guardCase(builder);218builder.setInsertionPointAfter(operation);219220if constexpr (std::is_same_v<AfterOpTy, mlir::acc::DeleteOp> ||221std::is_same_v<AfterOpTy, mlir::acc::DetachOp>) {222// Detach/Delete ops don't have the variable reference here, so they223// take 1 fewer argument to their build function.224afterOp = builder.create<AfterOpTy>(225opInfo.beginLoc, beforeOp.getResult(), structured, implicit,226opInfo.name, opInfo.bounds);227} else {228afterOp = builder.create<AfterOpTy>(229opInfo.beginLoc, beforeOp.getResult(), opInfo.varValue, structured,230implicit, opInfo.name, opInfo.bounds);231}232}233234// Set the 'rest' of the info for both operations.235beforeOp.setDataClause(dataClause);236afterOp.setDataClause(dataClause);237beforeOp.setModifiers(convertModifiers(modifiers));238afterOp.setModifiers(convertModifiers(modifiers));239240// Make sure we record these, so 'async' values can be updated later.241dataOperands.push_back(beforeOp.getOperation());242dataOperands.push_back(afterOp.getOperation());243}244245template <typename BeforeOpTy>246void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,247OpenACCModifierKind modifiers, bool structured,248bool implicit) {249CIRGenFunction::OpenACCDataOperandInfo opInfo =250cgf.getOpenACCDataOperandInfo(varOperand);251auto beforeOp =252builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,253implicit, opInfo.name, opInfo.bounds);254operation.getDataClauseOperandsMutable().append(beforeOp.getResult());255256// Set the 'rest' of the info for the operation.257beforeOp.setDataClause(dataClause);258beforeOp.setModifiers(convertModifiers(modifiers));259260// Make sure we record these, so 'async' values can be updated later.261dataOperands.push_back(beforeOp.getOperation());262}263264// Helper function that covers for the fact that we don't have this function265// on all operation types.266mlir::ArrayAttr getAsyncOnlyAttr() {267if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,268mlir::acc::KernelsOp, mlir::acc::DataOp,269mlir::acc::UpdateOp>) {270return operation.getAsyncOnlyAttr();271} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,272mlir::acc::ExitDataOp>) {273if (!operation.getAsyncAttr())274return mlir::ArrayAttr{};275276llvm::SmallVector<mlir::Attribute> devTysTemp;277devTysTemp.push_back(mlir::acc::DeviceTypeAttr::get(278builder.getContext(), mlir::acc::DeviceType::None));279return mlir::ArrayAttr::get(builder.getContext(), devTysTemp);280} else if constexpr (isCombinedType<OpTy>) {281return operation.computeOp.getAsyncOnlyAttr();282}283284// Note: 'wait' has async as well, but it cannot have data clauses, so we285// don't have to handle them here.286287llvm_unreachable("getting asyncOnly when clause not valid on operation?");288}289290// Helper function that covers for the fact that we don't have this function291// on all operation types.292mlir::ArrayAttr getAsyncOperandsDeviceTypeAttr() {293if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,294mlir::acc::KernelsOp, mlir::acc::DataOp,295mlir::acc::UpdateOp>) {296return operation.getAsyncOperandsDeviceTypeAttr();297} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,298mlir::acc::ExitDataOp>) {299if (!operation.getAsyncOperand())300return mlir::ArrayAttr{};301302llvm::SmallVector<mlir::Attribute> devTysTemp;303devTysTemp.push_back(mlir::acc::DeviceTypeAttr::get(304builder.getContext(), mlir::acc::DeviceType::None));305return mlir::ArrayAttr::get(builder.getContext(), devTysTemp);306} else if constexpr (isCombinedType<OpTy>) {307return operation.computeOp.getAsyncOperandsDeviceTypeAttr();308}309310// Note: 'wait' has async as well, but it cannot have data clauses, so we311// don't have to handle them here.312313llvm_unreachable(314"getting asyncOperandsDeviceType when clause not valid on operation?");315}316317// Helper function that covers for the fact that we don't have this function318// on all operation types.319mlir::OperandRange getAsyncOperands() {320if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,321mlir::acc::KernelsOp, mlir::acc::DataOp,322mlir::acc::UpdateOp>)323return operation.getAsyncOperands();324else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,325mlir::acc::ExitDataOp>)326return operation.getAsyncOperandMutable();327else if constexpr (isCombinedType<OpTy>)328return operation.computeOp.getAsyncOperands();329330// Note: 'wait' has async as well, but it cannot have data clauses, so we331// don't have to handle them here.332333llvm_unreachable(334"getting asyncOperandsDeviceType when clause not valid on operation?");335}336337// The 'data' clauses all require that we add the 'async' values from the338// operation to them. We've collected the data operands along the way, so use339// that list to get the current 'async' values.340void updateDataOperandAsyncValues() {341if (!hasAsyncClause || dataOperands.empty())342return;343344for (mlir::Operation *dataOp : dataOperands) {345llvm::TypeSwitch<mlir::Operation *, void>(dataOp)346.Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto op) {347op.setAsyncOnlyAttr(getAsyncOnlyAttr());348op.setAsyncOperandsDeviceTypeAttr(getAsyncOperandsDeviceTypeAttr());349op.getAsyncOperandsMutable().assign(getAsyncOperands());350})351.Default([&](mlir::Operation *) {352llvm_unreachable("Not a data operation?");353});354}355}356357public:358OpenACCClauseCIREmitter(OpTy &operation, CIRGen::CIRGenFunction &cgf,359CIRGen::CIRGenBuilderTy &builder,360OpenACCDirectiveKind dirKind, SourceLocation dirLoc)361: operation(operation), cgf(cgf), builder(builder), dirKind(dirKind),362dirLoc(dirLoc) {}363364void VisitClause(const OpenACCClause &clause) {365clauseNotImplemented(clause);366}367368// The entry point for the CIR emitter. All users should use this rather than369// 'visitClauseList', as this also handles the things that have to happen370// 'after' the clauses are all visited.371void emitClauses(ArrayRef<const OpenACCClause *> clauses) {372this->VisitClauseList(clauses);373updateDataOperandAsyncValues();374}375376void VisitDefaultClause(const OpenACCDefaultClause &clause) {377// This type-trait checks if 'op'(the first arg) is one of the mlir::acc378// operations listed in the rest of the arguments.379if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,380mlir::acc::KernelsOp, mlir::acc::DataOp>) {381switch (clause.getDefaultClauseKind()) {382case OpenACCDefaultClauseKind::None:383operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::None);384break;385case OpenACCDefaultClauseKind::Present:386operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::Present);387break;388case OpenACCDefaultClauseKind::Invalid:389break;390}391} else if constexpr (isCombinedType<OpTy>) {392applyToComputeOp(clause);393} else {394llvm_unreachable("Unknown construct kind in VisitDefaultClause");395}396}397398void VisitDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {399setLastDeviceTypeClause(clause);400401if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp,402mlir::acc::ShutdownOp>) {403for (const DeviceTypeArgument &arg : clause.getArchitectures())404operation.addDeviceType(builder.getContext(),405decodeDeviceType(arg.getIdentifierInfo()));406} else if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {407assert(!operation.getDeviceTypeAttr() && "already have device-type?");408assert(clause.getArchitectures().size() <= 1);409410if (!clause.getArchitectures().empty())411operation.setDeviceType(412decodeDeviceType(clause.getArchitectures()[0].getIdentifierInfo()));413} else if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,414mlir::acc::SerialOp, mlir::acc::KernelsOp,415mlir::acc::DataOp, mlir::acc::LoopOp,416mlir::acc::UpdateOp>) {417// Nothing to do here, these constructs don't have any IR for these, as418// they just modify the other clauses IR. So setting of419// `lastDeviceTypeValues` (done above) is all we need.420} else if constexpr (isCombinedType<OpTy>) {421// Nothing to do here either, combined constructs are just going to use422// 'lastDeviceTypeValues' to set the value for the child visitor.423} else {424// TODO: When we've implemented this for everything, switch this to an425// unreachable. routine construct remains.426return clauseNotImplemented(clause);427}428}429430void VisitNumWorkersClause(const OpenACCNumWorkersClause &clause) {431if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,432mlir::acc::KernelsOp>) {433operation.addNumWorkersOperand(builder.getContext(),434emitIntExpr(clause.getIntExpr()),435lastDeviceTypeValues);436} else if constexpr (isCombinedType<OpTy>) {437applyToComputeOp(clause);438} else {439llvm_unreachable("Unknown construct kind in VisitNumGangsClause");440}441}442443void VisitVectorLengthClause(const OpenACCVectorLengthClause &clause) {444if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,445mlir::acc::KernelsOp>) {446operation.addVectorLengthOperand(builder.getContext(),447emitIntExpr(clause.getIntExpr()),448lastDeviceTypeValues);449} else if constexpr (isCombinedType<OpTy>) {450applyToComputeOp(clause);451} else {452llvm_unreachable("Unknown construct kind in VisitVectorLengthClause");453}454}455456void VisitAsyncClause(const OpenACCAsyncClause &clause) {457hasAsyncClause = true;458if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,459mlir::acc::KernelsOp, mlir::acc::DataOp,460mlir::acc::EnterDataOp, mlir::acc::ExitDataOp,461mlir::acc::UpdateOp>) {462if (!clause.hasIntExpr()) {463operation.addAsyncOnly(builder.getContext(), lastDeviceTypeValues);464} else {465466mlir::Value intExpr;467{468// Async int exprs can be referenced by the data operands, which means469// that the int-exprs have to appear before them. IF there is a data470// operand already, set the insertion point to 'before' it.471mlir::OpBuilder::InsertionGuard guardCase(builder);472if (!dataOperands.empty())473builder.setInsertionPoint(dataOperands.front());474intExpr = emitIntExpr(clause.getIntExpr());475}476operation.addAsyncOperand(builder.getContext(), intExpr,477lastDeviceTypeValues);478}479} else if constexpr (isOneOfTypes<OpTy, mlir::acc::WaitOp>) {480// Wait doesn't have a device_type, so its handling here is slightly481// different.482if (!clause.hasIntExpr())483operation.setAsync(true);484else485operation.getAsyncOperandMutable().append(486emitIntExpr(clause.getIntExpr()));487} else if constexpr (isCombinedType<OpTy>) {488applyToComputeOp(clause);489} else {490// TODO: When we've implemented this for everything, switch this to an491// unreachable. Combined constructs remain. update construct remains.492return clauseNotImplemented(clause);493}494}495496void VisitSelfClause(const OpenACCSelfClause &clause) {497if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,498mlir::acc::KernelsOp>) {499if (clause.isEmptySelfClause()) {500operation.setSelfAttr(true);501} else if (clause.isConditionExprClause()) {502assert(clause.hasConditionExpr());503operation.getSelfCondMutable().append(504createCondition(clause.getConditionExpr()));505} else {506llvm_unreachable("var-list version of self shouldn't get here");507}508} else if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {509assert(!clause.isEmptySelfClause() && !clause.isConditionExprClause() &&510"var-list version of self required for update");511for (const Expr *var : clause.getVarList())512addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::UpdateHostOp>(513var, mlir::acc::DataClause::acc_update_self, {},514/*structured=*/false, /*implicit=*/false);515} else if constexpr (isCombinedType<OpTy>) {516applyToComputeOp(clause);517} else {518llvm_unreachable("Unknown construct kind in VisitSelfClause");519}520}521522void VisitHostClause(const OpenACCHostClause &clause) {523if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {524for (const Expr *var : clause.getVarList())525addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::UpdateHostOp>(526var, mlir::acc::DataClause::acc_update_host, {},527/*structured=*/false, /*implicit=*/false);528} else {529llvm_unreachable("Unknown construct kind in VisitHostClause");530}531}532533void VisitDeviceClause(const OpenACCDeviceClause &clause) {534if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {535for (const Expr *var : clause.getVarList())536addDataOperand<mlir::acc::UpdateDeviceOp>(537var, mlir::acc::DataClause::acc_update_device, {},538/*structured=*/false, /*implicit=*/false);539} else {540llvm_unreachable("Unknown construct kind in VisitDeviceClause");541}542}543544void VisitIfClause(const OpenACCIfClause &clause) {545if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,546mlir::acc::KernelsOp, mlir::acc::InitOp,547mlir::acc::ShutdownOp, mlir::acc::SetOp,548mlir::acc::DataOp, mlir::acc::WaitOp,549mlir::acc::HostDataOp, mlir::acc::EnterDataOp,550mlir::acc::ExitDataOp, mlir::acc::UpdateOp>) {551operation.getIfCondMutable().append(552createCondition(clause.getConditionExpr()));553} else if constexpr (isCombinedType<OpTy>) {554applyToComputeOp(clause);555} else {556llvm_unreachable("Unknown construct kind in VisitIfClause");557}558}559560void VisitIfPresentClause(const OpenACCIfPresentClause &clause) {561if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp,562mlir::acc::UpdateOp>) {563operation.setIfPresent(true);564} else {565llvm_unreachable("unknown construct kind in VisitIfPresentClause");566}567}568569void VisitDeviceNumClause(const OpenACCDeviceNumClause &clause) {570if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp, mlir::acc::ShutdownOp,571mlir::acc::SetOp>) {572operation.getDeviceNumMutable().append(emitIntExpr(clause.getIntExpr()));573} else {574llvm_unreachable(575"init, shutdown, set, are only valid device_num constructs");576}577}578579void VisitNumGangsClause(const OpenACCNumGangsClause &clause) {580if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,581mlir::acc::KernelsOp>) {582llvm::SmallVector<mlir::Value> values;583for (const Expr *E : clause.getIntExprs())584values.push_back(emitIntExpr(E));585586operation.addNumGangsOperands(builder.getContext(), values,587lastDeviceTypeValues);588} else if constexpr (isCombinedType<OpTy>) {589applyToComputeOp(clause);590} else {591llvm_unreachable("Unknown construct kind in VisitNumGangsClause");592}593}594595void VisitWaitClause(const OpenACCWaitClause &clause) {596if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,597mlir::acc::KernelsOp, mlir::acc::DataOp,598mlir::acc::EnterDataOp, mlir::acc::ExitDataOp,599mlir::acc::UpdateOp>) {600if (!clause.hasExprs()) {601operation.addWaitOnly(builder.getContext(), lastDeviceTypeValues);602} else {603llvm::SmallVector<mlir::Value> values;604if (clause.hasDevNumExpr())605values.push_back(emitIntExpr(clause.getDevNumExpr()));606for (const Expr *E : clause.getQueueIdExprs())607values.push_back(emitIntExpr(E));608operation.addWaitOperands(builder.getContext(), clause.hasDevNumExpr(),609values, lastDeviceTypeValues);610}611} else if constexpr (isCombinedType<OpTy>) {612applyToComputeOp(clause);613} else {614// TODO: When we've implemented this for everything, switch this to an615// unreachable. update construct remains.616return clauseNotImplemented(clause);617}618}619620void VisitDefaultAsyncClause(const OpenACCDefaultAsyncClause &clause) {621if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {622operation.getDefaultAsyncMutable().append(623emitIntExpr(clause.getIntExpr()));624} else {625llvm_unreachable("set, is only valid device_num constructs");626}627}628629void VisitSeqClause(const OpenACCSeqClause &clause) {630if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {631operation.addSeq(builder.getContext(), lastDeviceTypeValues);632} else if constexpr (isCombinedType<OpTy>) {633applyToLoopOp(clause);634} else {635// TODO: When we've implemented this for everything, switch this to an636// unreachable. Routine construct remains.637return clauseNotImplemented(clause);638}639}640641void VisitAutoClause(const OpenACCAutoClause &clause) {642if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {643operation.addAuto(builder.getContext(), lastDeviceTypeValues);644} else if constexpr (isCombinedType<OpTy>) {645applyToLoopOp(clause);646} else {647// TODO: When we've implemented this for everything, switch this to an648// unreachable. Routine, construct remains.649return clauseNotImplemented(clause);650}651}652653void VisitIndependentClause(const OpenACCIndependentClause &clause) {654if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {655operation.addIndependent(builder.getContext(), lastDeviceTypeValues);656} else if constexpr (isCombinedType<OpTy>) {657applyToLoopOp(clause);658} else {659// TODO: When we've implemented this for everything, switch this to an660// unreachable. Routine construct remains.661return clauseNotImplemented(clause);662}663}664665void VisitCollapseClause(const OpenACCCollapseClause &clause) {666if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {667llvm::APInt value =668clause.getIntExpr()->EvaluateKnownConstInt(cgf.cgm.getASTContext());669670value = value.sextOrTrunc(64);671operation.setCollapseForDeviceTypes(builder.getContext(),672lastDeviceTypeValues, value);673} else if constexpr (isCombinedType<OpTy>) {674applyToLoopOp(clause);675} else {676llvm_unreachable("Unknown construct kind in VisitCollapseClause");677}678}679680void VisitTileClause(const OpenACCTileClause &clause) {681if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {682llvm::SmallVector<mlir::Value> values;683684for (const Expr *e : clause.getSizeExprs()) {685mlir::Location exprLoc = cgf.cgm.getLoc(e->getBeginLoc());686687// We represent the * as -1. Additionally, this is a constant, so we688// can always just emit it as 64 bits to avoid having to do any more689// work to determine signedness or size.690if (isa<OpenACCAsteriskSizeExpr>(e)) {691values.push_back(createConstantInt(exprLoc, 64, -1));692} else {693llvm::APInt curValue =694e->EvaluateKnownConstInt(cgf.cgm.getASTContext());695values.push_back(createConstantInt(696exprLoc, 64, curValue.sextOrTrunc(64).getSExtValue()));697}698}699700operation.setTileForDeviceTypes(builder.getContext(),701lastDeviceTypeValues, values);702} else if constexpr (isCombinedType<OpTy>) {703applyToLoopOp(clause);704} else {705llvm_unreachable("Unknown construct kind in VisitTileClause");706}707}708709void VisitWorkerClause(const OpenACCWorkerClause &clause) {710if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {711if (clause.hasIntExpr())712operation.addWorkerNumOperand(builder.getContext(),713emitIntExpr(clause.getIntExpr()),714lastDeviceTypeValues);715else716operation.addEmptyWorker(builder.getContext(), lastDeviceTypeValues);717718} else if constexpr (isCombinedType<OpTy>) {719applyToLoopOp(clause);720} else {721// TODO: When we've implemented this for everything, switch this to an722// unreachable. Combined constructs remain.723return clauseNotImplemented(clause);724}725}726727void VisitVectorClause(const OpenACCVectorClause &clause) {728if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {729if (clause.hasIntExpr())730operation.addVectorOperand(builder.getContext(),731emitIntExpr(clause.getIntExpr()),732lastDeviceTypeValues);733else734operation.addEmptyVector(builder.getContext(), lastDeviceTypeValues);735736} else if constexpr (isCombinedType<OpTy>) {737applyToLoopOp(clause);738} else {739// TODO: When we've implemented this for everything, switch this to an740// unreachable. Combined constructs remain.741return clauseNotImplemented(clause);742}743}744745void VisitGangClause(const OpenACCGangClause &clause) {746if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {747if (clause.getNumExprs() == 0) {748operation.addEmptyGang(builder.getContext(), lastDeviceTypeValues);749} else {750llvm::SmallVector<mlir::Value> values;751llvm::SmallVector<mlir::acc::GangArgType> argTypes;752for (unsigned i : llvm::index_range(0u, clause.getNumExprs())) {753auto [kind, expr] = clause.getExpr(i);754mlir::Location exprLoc = cgf.cgm.getLoc(expr->getBeginLoc());755argTypes.push_back(decodeGangType(kind));756if (kind == OpenACCGangKind::Dim) {757llvm::APInt curValue =758expr->EvaluateKnownConstInt(cgf.cgm.getASTContext());759// The value is 1, 2, or 3, but the type isn't necessarily smaller760// than 64.761curValue = curValue.sextOrTrunc(64);762values.push_back(763createConstantInt(exprLoc, 64, curValue.getSExtValue()));764} else if (isa<OpenACCAsteriskSizeExpr>(expr)) {765values.push_back(createConstantInt(exprLoc, 64, -1));766} else {767values.push_back(emitIntExpr(expr));768}769}770771operation.addGangOperands(builder.getContext(), lastDeviceTypeValues,772argTypes, values);773}774} else if constexpr (isCombinedType<OpTy>) {775applyToLoopOp(clause);776} else {777llvm_unreachable("Unknown construct kind in VisitGangClause");778}779}780781void VisitCopyClause(const OpenACCCopyClause &clause) {782if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,783mlir::acc::KernelsOp, mlir::acc::DataOp>) {784for (const Expr *var : clause.getVarList())785addDataOperand<mlir::acc::CopyinOp, mlir::acc::CopyoutOp>(786var, mlir::acc::DataClause::acc_copy, clause.getModifierList(),787/*structured=*/true,788/*implicit=*/false);789} else if constexpr (isCombinedType<OpTy>) {790applyToComputeOp(clause);791} else {792// TODO: When we've implemented this for everything, switch this to an793// unreachable. declare construct remains.794return clauseNotImplemented(clause);795}796}797798void VisitCopyInClause(const OpenACCCopyInClause &clause) {799if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,800mlir::acc::KernelsOp, mlir::acc::DataOp>) {801for (const Expr *var : clause.getVarList())802addDataOperand<mlir::acc::CopyinOp, mlir::acc::DeleteOp>(803var, mlir::acc::DataClause::acc_copyin, clause.getModifierList(),804/*structured=*/true,805/*implicit=*/false);806} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp>) {807for (const Expr *var : clause.getVarList())808addDataOperand<mlir::acc::CopyinOp>(809var, mlir::acc::DataClause::acc_copyin, clause.getModifierList(),810/*structured=*/false, /*implicit=*/false);811} else if constexpr (isCombinedType<OpTy>) {812applyToComputeOp(clause);813} else {814// TODO: When we've implemented this for everything, switch this to an815// unreachable. declare construct remains.816return clauseNotImplemented(clause);817}818}819820void VisitCopyOutClause(const OpenACCCopyOutClause &clause) {821if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,822mlir::acc::KernelsOp, mlir::acc::DataOp>) {823for (const Expr *var : clause.getVarList())824addDataOperand<mlir::acc::CreateOp, mlir::acc::CopyoutOp>(825var, mlir::acc::DataClause::acc_copyout, clause.getModifierList(),826/*structured=*/true,827/*implicit=*/false);828} else if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {829for (const Expr *var : clause.getVarList())830addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::CopyoutOp>(831var, mlir::acc::DataClause::acc_copyout, clause.getModifierList(),832/*structured=*/false,833/*implicit=*/false);834} else if constexpr (isCombinedType<OpTy>) {835applyToComputeOp(clause);836} else {837// TODO: When we've implemented this for everything, switch this to an838// unreachable. declare construct remains.839return clauseNotImplemented(clause);840}841}842843void VisitCreateClause(const OpenACCCreateClause &clause) {844if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,845mlir::acc::KernelsOp, mlir::acc::DataOp>) {846for (const Expr *var : clause.getVarList())847addDataOperand<mlir::acc::CreateOp, mlir::acc::DeleteOp>(848var, mlir::acc::DataClause::acc_create, clause.getModifierList(),849/*structured=*/true,850/*implicit=*/false);851} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp>) {852for (const Expr *var : clause.getVarList())853addDataOperand<mlir::acc::CreateOp>(854var, mlir::acc::DataClause::acc_create, clause.getModifierList(),855/*structured=*/false, /*implicit=*/false);856} else if constexpr (isCombinedType<OpTy>) {857applyToComputeOp(clause);858} else {859// TODO: When we've implemented this for everything, switch this to an860// unreachable. declare construct remains.861return clauseNotImplemented(clause);862}863}864865void VisitDeleteClause(const OpenACCDeleteClause &clause) {866if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {867for (const Expr *var : clause.getVarList())868addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::DeleteOp>(869var, mlir::acc::DataClause::acc_delete, {},870/*structured=*/false,871/*implicit=*/false);872} else {873llvm_unreachable("Unknown construct kind in VisitDeleteClause");874}875}876877void VisitDetachClause(const OpenACCDetachClause &clause) {878if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {879for (const Expr *var : clause.getVarList())880addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::DetachOp>(881var, mlir::acc::DataClause::acc_detach, {},882/*structured=*/false,883/*implicit=*/false);884} else {885llvm_unreachable("Unknown construct kind in VisitDetachClause");886}887}888889void VisitFinalizeClause(const OpenACCFinalizeClause &clause) {890if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {891operation.setFinalize(true);892} else {893llvm_unreachable("Unknown construct kind in VisitFinalizeClause");894}895}896897void VisitUseDeviceClause(const OpenACCUseDeviceClause &clause) {898if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp>) {899for (const Expr *var : clause.getVarList())900addDataOperand<mlir::acc::UseDeviceOp>(901var, mlir::acc::DataClause::acc_use_device, {}, /*structured=*/true,902/*implicit=*/false);903} else {904llvm_unreachable("Unknown construct kind in VisitUseDeviceClause");905}906}907908void VisitDevicePtrClause(const OpenACCDevicePtrClause &clause) {909if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,910mlir::acc::KernelsOp, mlir::acc::DataOp>) {911for (const Expr *var : clause.getVarList())912addDataOperand<mlir::acc::DevicePtrOp>(913var, mlir::acc::DataClause::acc_deviceptr, {},914/*structured=*/true,915/*implicit=*/false);916} else if constexpr (isCombinedType<OpTy>) {917applyToComputeOp(clause);918} else {919// TODO: When we've implemented this for everything, switch this to an920// unreachable. declare remains.921return clauseNotImplemented(clause);922}923}924925void VisitNoCreateClause(const OpenACCNoCreateClause &clause) {926if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,927mlir::acc::KernelsOp, mlir::acc::DataOp>) {928for (const Expr *var : clause.getVarList())929addDataOperand<mlir::acc::NoCreateOp, mlir::acc::DeleteOp>(930var, mlir::acc::DataClause::acc_no_create, {}, /*structured=*/true,931/*implicit=*/false);932} else if constexpr (isCombinedType<OpTy>) {933applyToComputeOp(clause);934} else {935llvm_unreachable("Unknown construct kind in VisitNoCreateClause");936}937}938939void VisitPresentClause(const OpenACCPresentClause &clause) {940if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,941mlir::acc::KernelsOp, mlir::acc::DataOp>) {942for (const Expr *var : clause.getVarList())943addDataOperand<mlir::acc::PresentOp, mlir::acc::DeleteOp>(944var, mlir::acc::DataClause::acc_present, {}, /*structured=*/true,945/*implicit=*/false);946} else if constexpr (isCombinedType<OpTy>) {947applyToComputeOp(clause);948} else {949// TODO: When we've implemented this for everything, switch this to an950// unreachable. declare remains.951return clauseNotImplemented(clause);952}953}954955void VisitAttachClause(const OpenACCAttachClause &clause) {956if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,957mlir::acc::KernelsOp, mlir::acc::DataOp>) {958for (const Expr *var : clause.getVarList())959addDataOperand<mlir::acc::AttachOp, mlir::acc::DetachOp>(960var, mlir::acc::DataClause::acc_attach, {}, /*structured=*/true,961/*implicit=*/false);962} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp>) {963for (const Expr *var : clause.getVarList())964addDataOperand<mlir::acc::AttachOp>(965var, mlir::acc::DataClause::acc_attach, {},966/*structured=*/false, /*implicit=*/false);967} else if constexpr (isCombinedType<OpTy>) {968applyToComputeOp(clause);969} else {970llvm_unreachable("Unknown construct kind in VisitAttachClause");971}972}973};974975template <typename OpTy>976auto makeClauseEmitter(OpTy &op, CIRGen::CIRGenFunction &cgf,977CIRGen::CIRGenBuilderTy &builder,978OpenACCDirectiveKind dirKind, SourceLocation dirLoc) {979return OpenACCClauseCIREmitter<OpTy>(op, cgf, builder, dirKind, dirLoc);980}981} // namespace982983template <typename Op>984void CIRGenFunction::emitOpenACCClauses(985Op &op, OpenACCDirectiveKind dirKind, SourceLocation dirLoc,986ArrayRef<const OpenACCClause *> clauses) {987mlir::OpBuilder::InsertionGuard guardCase(builder);988989// Sets insertion point before the 'op', since every new expression needs to990// be before the operation.991builder.setInsertionPoint(op);992makeClauseEmitter(op, *this, builder, dirKind, dirLoc).emitClauses(clauses);993}994995#define EXPL_SPEC(N) \996template void CIRGenFunction::emitOpenACCClauses<N>( \997N &, OpenACCDirectiveKind, SourceLocation, \998ArrayRef<const OpenACCClause *>);999EXPL_SPEC(mlir::acc::ParallelOp)1000EXPL_SPEC(mlir::acc::SerialOp)1001EXPL_SPEC(mlir::acc::KernelsOp)1002EXPL_SPEC(mlir::acc::LoopOp)1003EXPL_SPEC(mlir::acc::DataOp)1004EXPL_SPEC(mlir::acc::InitOp)1005EXPL_SPEC(mlir::acc::ShutdownOp)1006EXPL_SPEC(mlir::acc::SetOp)1007EXPL_SPEC(mlir::acc::WaitOp)1008EXPL_SPEC(mlir::acc::HostDataOp)1009EXPL_SPEC(mlir::acc::EnterDataOp)1010EXPL_SPEC(mlir::acc::ExitDataOp)1011EXPL_SPEC(mlir::acc::UpdateOp)1012#undef EXPL_SPEC10131014template <typename ComputeOp, typename LoopOp>1015void CIRGenFunction::emitOpenACCClauses(1016ComputeOp &op, LoopOp &loopOp, OpenACCDirectiveKind dirKind,1017SourceLocation dirLoc, ArrayRef<const OpenACCClause *> clauses) {1018static_assert(std::is_same_v<mlir::acc::LoopOp, LoopOp>);10191020CombinedConstructClauseInfo<ComputeOp> inf{op, loopOp};1021// We cannot set the insertion point here and do so in the emitter, but make1022// sure we reset it with the 'guard' anyway.1023mlir::OpBuilder::InsertionGuard guardCase(builder);1024makeClauseEmitter(inf, *this, builder, dirKind, dirLoc).emitClauses(clauses);1025}10261027#define EXPL_SPEC(N) \1028template void CIRGenFunction::emitOpenACCClauses<N, mlir::acc::LoopOp>( \1029N &, mlir::acc::LoopOp &, OpenACCDirectiveKind, SourceLocation, \1030ArrayRef<const OpenACCClause *>);10311032EXPL_SPEC(mlir::acc::ParallelOp)1033EXPL_SPEC(mlir::acc::SerialOp)1034EXPL_SPEC(mlir::acc::KernelsOp)1035#undef EXPL_SPEC103610371038