1f674ddc1SPeter Klausler //===-- lib/Semantics/check-cuda.cpp ----------------------------*- C++ -*-===// 2f674ddc1SPeter Klausler // 3f674ddc1SPeter Klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4f674ddc1SPeter Klausler // See https://llvm.org/LICENSE.txt for license information. 5f674ddc1SPeter Klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6f674ddc1SPeter Klausler // 7f674ddc1SPeter Klausler //===----------------------------------------------------------------------===// 8f674ddc1SPeter Klausler 9f674ddc1SPeter Klausler #include "check-cuda.h" 10f674ddc1SPeter Klausler #include "flang/Common/template.h" 11f674ddc1SPeter Klausler #include "flang/Evaluate/fold.h" 120aa982fbSValentin Clement (バレンタイン クレメン) #include "flang/Evaluate/tools.h" 13f674ddc1SPeter Klausler #include "flang/Evaluate/traverse.h" 14f674ddc1SPeter Klausler #include "flang/Parser/parse-tree-visitor.h" 15f674ddc1SPeter Klausler #include "flang/Parser/parse-tree.h" 16f674ddc1SPeter Klausler #include "flang/Parser/tools.h" 17f674ddc1SPeter Klausler #include "flang/Semantics/expression.h" 18f674ddc1SPeter Klausler #include "flang/Semantics/symbol.h" 190aa982fbSValentin Clement (バレンタイン クレメン) #include "flang/Semantics/tools.h" 20f674ddc1SPeter Klausler 21f674ddc1SPeter Klausler // Once labeled DO constructs have been canonicalized and their parse subtrees 22f674ddc1SPeter Klausler // transformed into parser::DoConstructs, scan the parser::Blocks of the program 23f674ddc1SPeter Klausler // and merge adjacent CUFKernelDoConstructs and DoConstructs whenever the 24f674ddc1SPeter Klausler // CUFKernelDoConstruct doesn't already have an embedded DoConstruct. Also 25f674ddc1SPeter Klausler // emit errors about improper or missing DoConstructs. 26f674ddc1SPeter Klausler 27f674ddc1SPeter Klausler namespace Fortran::parser { 28f674ddc1SPeter Klausler struct Mutator { 29f674ddc1SPeter Klausler template <typename A> bool Pre(A &) { return true; } 30f674ddc1SPeter Klausler template <typename A> void Post(A &) {} 31f674ddc1SPeter Klausler bool Pre(Block &); 32f674ddc1SPeter Klausler }; 33f674ddc1SPeter Klausler 34f674ddc1SPeter Klausler bool Mutator::Pre(Block &block) { 35f674ddc1SPeter Klausler for (auto iter{block.begin()}; iter != block.end(); ++iter) { 36f674ddc1SPeter Klausler if (auto *kernel{Unwrap<CUFKernelDoConstruct>(*iter)}) { 37f674ddc1SPeter Klausler auto &nested{std::get<std::optional<DoConstruct>>(kernel->t)}; 38f674ddc1SPeter Klausler if (!nested) { 39f674ddc1SPeter Klausler if (auto next{iter}; ++next != block.end()) { 40f674ddc1SPeter Klausler if (auto *doConstruct{Unwrap<DoConstruct>(*next)}) { 41f674ddc1SPeter Klausler nested = std::move(*doConstruct); 42f674ddc1SPeter Klausler block.erase(next); 43f674ddc1SPeter Klausler } 44f674ddc1SPeter Klausler } 45f674ddc1SPeter Klausler } 46f674ddc1SPeter Klausler } else { 47f674ddc1SPeter Klausler Walk(*iter, *this); 48f674ddc1SPeter Klausler } 49f674ddc1SPeter Klausler } 50f674ddc1SPeter Klausler return false; 51f674ddc1SPeter Klausler } 52f674ddc1SPeter Klausler } // namespace Fortran::parser 53f674ddc1SPeter Klausler 54f674ddc1SPeter Klausler namespace Fortran::semantics { 55f674ddc1SPeter Klausler 56f674ddc1SPeter Klausler bool CanonicalizeCUDA(parser::Program &program) { 57f674ddc1SPeter Klausler parser::Mutator mutator; 58f674ddc1SPeter Klausler parser::Walk(program, mutator); 59f674ddc1SPeter Klausler return true; 60f674ddc1SPeter Klausler } 61f674ddc1SPeter Klausler 62f674ddc1SPeter Klausler using MaybeMsg = std::optional<parser::MessageFormattedText>; 63f674ddc1SPeter Klausler 64f674ddc1SPeter Klausler // Traverses an evaluate::Expr<> in search of unsupported operations 65f674ddc1SPeter Klausler // on the device. 66f674ddc1SPeter Klausler 67f674ddc1SPeter Klausler struct DeviceExprChecker 68f674ddc1SPeter Klausler : public evaluate::AnyTraverse<DeviceExprChecker, MaybeMsg> { 69f674ddc1SPeter Klausler using Result = MaybeMsg; 70f674ddc1SPeter Klausler using Base = evaluate::AnyTraverse<DeviceExprChecker, Result>; 71f674ddc1SPeter Klausler DeviceExprChecker() : Base(*this) {} 72f674ddc1SPeter Klausler using Base::operator(); 73f674ddc1SPeter Klausler Result operator()(const evaluate::ProcedureDesignator &x) const { 74f674ddc1SPeter Klausler if (const Symbol * sym{x.GetInterfaceSymbol()}) { 75f674ddc1SPeter Klausler const auto *subp{ 76f674ddc1SPeter Klausler sym->GetUltimate().detailsIf<semantics::SubprogramDetails>()}; 77f674ddc1SPeter Klausler if (subp) { 78f674ddc1SPeter Klausler if (auto attrs{subp->cudaSubprogramAttrs()}) { 79f674ddc1SPeter Klausler if (*attrs == common::CUDASubprogramAttrs::HostDevice || 80f674ddc1SPeter Klausler *attrs == common::CUDASubprogramAttrs::Device) { 81f674ddc1SPeter Klausler return {}; 82f674ddc1SPeter Klausler } 83f674ddc1SPeter Klausler } 84f674ddc1SPeter Klausler } 85f674ddc1SPeter Klausler } else if (x.GetSpecificIntrinsic()) { 86f674ddc1SPeter Klausler // TODO(CUDA): Check for unsupported intrinsics here 87f674ddc1SPeter Klausler return {}; 88f674ddc1SPeter Klausler } 89f674ddc1SPeter Klausler return parser::MessageFormattedText( 90f674ddc1SPeter Klausler "'%s' may not be called in device code"_err_en_US, x.GetName()); 91f674ddc1SPeter Klausler } 92f674ddc1SPeter Klausler }; 93f674ddc1SPeter Klausler 9473216cd7SPeter Klausler struct FindHostArray 9573216cd7SPeter Klausler : public evaluate::AnyTraverse<FindHostArray, const Symbol *> { 9673216cd7SPeter Klausler using Result = const Symbol *; 9773216cd7SPeter Klausler using Base = evaluate::AnyTraverse<FindHostArray, Result>; 9873216cd7SPeter Klausler FindHostArray() : Base(*this) {} 9973216cd7SPeter Klausler using Base::operator(); 10073216cd7SPeter Klausler Result operator()(const evaluate::Component &x) const { 10173216cd7SPeter Klausler const Symbol &symbol{x.GetLastSymbol()}; 10273216cd7SPeter Klausler if (IsAllocatableOrPointer(symbol)) { 10373216cd7SPeter Klausler if (Result hostArray{(*this)(symbol)}) { 10473216cd7SPeter Klausler return hostArray; 10573216cd7SPeter Klausler } 10673216cd7SPeter Klausler } 10773216cd7SPeter Klausler return (*this)(x.base()); 10873216cd7SPeter Klausler } 10973216cd7SPeter Klausler Result operator()(const Symbol &symbol) const { 11073216cd7SPeter Klausler if (const auto *details{ 11173216cd7SPeter Klausler symbol.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()}) { 11273216cd7SPeter Klausler if (details->IsArray() && 11397b7baceSValentin Clement (バレンタイン クレメン) !symbol.attrs().test(Fortran::semantics::Attr::PARAMETER) && 11473216cd7SPeter Klausler (!details->cudaDataAttr() || 11573216cd7SPeter Klausler (details->cudaDataAttr() && 11673216cd7SPeter Klausler *details->cudaDataAttr() != common::CUDADataAttr::Device && 11781333cfcSValentin Clement (バレンタイン クレメン) *details->cudaDataAttr() != common::CUDADataAttr::Constant && 11873216cd7SPeter Klausler *details->cudaDataAttr() != common::CUDADataAttr::Managed && 11915c61a20SValentin Clement (バレンタイン クレメン) *details->cudaDataAttr() != common::CUDADataAttr::Shared && 12073216cd7SPeter Klausler *details->cudaDataAttr() != common::CUDADataAttr::Unified))) { 12173216cd7SPeter Klausler return &symbol; 12273216cd7SPeter Klausler } 12373216cd7SPeter Klausler } 12473216cd7SPeter Klausler return nullptr; 12573216cd7SPeter Klausler } 12673216cd7SPeter Klausler }; 12773216cd7SPeter Klausler 128f674ddc1SPeter Klausler template <typename A> static MaybeMsg CheckUnwrappedExpr(const A &x) { 129f674ddc1SPeter Klausler if (const auto *expr{parser::Unwrap<parser::Expr>(x)}) { 130f674ddc1SPeter Klausler return DeviceExprChecker{}(expr->typedExpr); 131f674ddc1SPeter Klausler } 132f674ddc1SPeter Klausler return {}; 133f674ddc1SPeter Klausler } 134f674ddc1SPeter Klausler 135f674ddc1SPeter Klausler template <typename A> 136f674ddc1SPeter Klausler static void CheckUnwrappedExpr( 137f674ddc1SPeter Klausler SemanticsContext &context, SourceName at, const A &x) { 138f674ddc1SPeter Klausler if (const auto *expr{parser::Unwrap<parser::Expr>(x)}) { 139f674ddc1SPeter Klausler if (auto msg{DeviceExprChecker{}(expr->typedExpr)}) { 140f674ddc1SPeter Klausler context.Say(at, std::move(*msg)); 141f674ddc1SPeter Klausler } 142f674ddc1SPeter Klausler } 143f674ddc1SPeter Klausler } 144f674ddc1SPeter Klausler 145f674ddc1SPeter Klausler template <bool CUF_KERNEL> struct ActionStmtChecker { 146f674ddc1SPeter Klausler template <typename A> static MaybeMsg WhyNotOk(const A &x) { 147f674ddc1SPeter Klausler if constexpr (ConstraintTrait<A>) { 148f674ddc1SPeter Klausler return WhyNotOk(x.thing); 149f674ddc1SPeter Klausler } else if constexpr (WrapperTrait<A>) { 150f674ddc1SPeter Klausler return WhyNotOk(x.v); 151f674ddc1SPeter Klausler } else if constexpr (UnionTrait<A>) { 152f674ddc1SPeter Klausler return WhyNotOk(x.u); 153f674ddc1SPeter Klausler } else if constexpr (TupleTrait<A>) { 154f674ddc1SPeter Klausler return WhyNotOk(x.t); 155f674ddc1SPeter Klausler } else { 156f674ddc1SPeter Klausler return parser::MessageFormattedText{ 157f674ddc1SPeter Klausler "Statement may not appear in device code"_err_en_US}; 158f674ddc1SPeter Klausler } 159f674ddc1SPeter Klausler } 160f674ddc1SPeter Klausler template <typename A> 161f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const common::Indirection<A> &x) { 162f674ddc1SPeter Klausler return WhyNotOk(x.value()); 163f674ddc1SPeter Klausler } 164f674ddc1SPeter Klausler template <typename... As> 165f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const std::variant<As...> &x) { 166f674ddc1SPeter Klausler return common::visit([](const auto &x) { return WhyNotOk(x); }, x); 167f674ddc1SPeter Klausler } 168f674ddc1SPeter Klausler template <std::size_t J = 0, typename... As> 169f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const std::tuple<As...> &x) { 170f674ddc1SPeter Klausler if constexpr (J == sizeof...(As)) { 171f674ddc1SPeter Klausler return {}; 172f674ddc1SPeter Klausler } else if (auto msg{WhyNotOk(std::get<J>(x))}) { 173f674ddc1SPeter Klausler return msg; 174f674ddc1SPeter Klausler } else { 175f674ddc1SPeter Klausler return WhyNotOk<(J + 1)>(x); 176f674ddc1SPeter Klausler } 177f674ddc1SPeter Klausler } 178f674ddc1SPeter Klausler template <typename A> static MaybeMsg WhyNotOk(const std::list<A> &x) { 179f674ddc1SPeter Klausler for (const auto &y : x) { 180f674ddc1SPeter Klausler if (MaybeMsg result{WhyNotOk(y)}) { 181f674ddc1SPeter Klausler return result; 182f674ddc1SPeter Klausler } 183f674ddc1SPeter Klausler } 184f674ddc1SPeter Klausler return {}; 185f674ddc1SPeter Klausler } 186f674ddc1SPeter Klausler template <typename A> static MaybeMsg WhyNotOk(const std::optional<A> &x) { 187f674ddc1SPeter Klausler if (x) { 188f674ddc1SPeter Klausler return WhyNotOk(*x); 189f674ddc1SPeter Klausler } else { 190f674ddc1SPeter Klausler return {}; 191f674ddc1SPeter Klausler } 192f674ddc1SPeter Klausler } 193f674ddc1SPeter Klausler template <typename A> 194f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::UnlabeledStatement<A> &x) { 195f674ddc1SPeter Klausler return WhyNotOk(x.statement); 196f674ddc1SPeter Klausler } 197f674ddc1SPeter Klausler template <typename A> 198f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::Statement<A> &x) { 199f674ddc1SPeter Klausler return WhyNotOk(x.statement); 200f674ddc1SPeter Klausler } 201f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::AllocateStmt &) { 202f674ddc1SPeter Klausler return {}; // AllocateObjects are checked elsewhere 203f674ddc1SPeter Klausler } 204f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::AllocateCoarraySpec &) { 205f674ddc1SPeter Klausler return parser::MessageFormattedText( 206f674ddc1SPeter Klausler "A coarray may not be allocated on the device"_err_en_US); 207f674ddc1SPeter Klausler } 208f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::DeallocateStmt &) { 209f674ddc1SPeter Klausler return {}; // AllocateObjects are checked elsewhere 210f674ddc1SPeter Klausler } 211f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::AssignmentStmt &x) { 212f674ddc1SPeter Klausler return DeviceExprChecker{}(x.typedAssignment); 213f674ddc1SPeter Klausler } 214f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::CallStmt &x) { 215f674ddc1SPeter Klausler return DeviceExprChecker{}(x.typedCall); 216f674ddc1SPeter Klausler } 217f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::ContinueStmt &) { return {}; } 218f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::IfStmt &x) { 219f674ddc1SPeter Klausler if (auto result{ 220f674ddc1SPeter Klausler CheckUnwrappedExpr(std::get<parser::ScalarLogicalExpr>(x.t))}) { 221f674ddc1SPeter Klausler return result; 222f674ddc1SPeter Klausler } 223f674ddc1SPeter Klausler return WhyNotOk( 224f674ddc1SPeter Klausler std::get<parser::UnlabeledStatement<parser::ActionStmt>>(x.t) 225f674ddc1SPeter Klausler .statement); 226f674ddc1SPeter Klausler } 227f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::NullifyStmt &x) { 228f674ddc1SPeter Klausler for (const auto &y : x.v) { 229f674ddc1SPeter Klausler if (MaybeMsg result{DeviceExprChecker{}(y.typedExpr)}) { 230f674ddc1SPeter Klausler return result; 231f674ddc1SPeter Klausler } 232f674ddc1SPeter Klausler } 233f674ddc1SPeter Klausler return {}; 234f674ddc1SPeter Klausler } 235f674ddc1SPeter Klausler static MaybeMsg WhyNotOk(const parser::PointerAssignmentStmt &x) { 236f674ddc1SPeter Klausler return DeviceExprChecker{}(x.typedAssignment); 237f674ddc1SPeter Klausler } 238f674ddc1SPeter Klausler }; 239f674ddc1SPeter Klausler 240f674ddc1SPeter Klausler template <bool IsCUFKernelDo> class DeviceContextChecker { 241f674ddc1SPeter Klausler public: 242f674ddc1SPeter Klausler explicit DeviceContextChecker(SemanticsContext &c) : context_{c} {} 243f674ddc1SPeter Klausler void CheckSubprogram(const parser::Name &name, const parser::Block &body) { 244f674ddc1SPeter Klausler if (name.symbol) { 245f674ddc1SPeter Klausler const auto *subp{ 246f674ddc1SPeter Klausler name.symbol->GetUltimate().detailsIf<SubprogramDetails>()}; 247f674ddc1SPeter Klausler if (subp && subp->moduleInterface()) { 248f674ddc1SPeter Klausler subp = subp->moduleInterface() 249f674ddc1SPeter Klausler ->GetUltimate() 250f674ddc1SPeter Klausler .detailsIf<SubprogramDetails>(); 251f674ddc1SPeter Klausler } 252f674ddc1SPeter Klausler if (subp && 253f674ddc1SPeter Klausler subp->cudaSubprogramAttrs().value_or( 254f674ddc1SPeter Klausler common::CUDASubprogramAttrs::Host) != 255f674ddc1SPeter Klausler common::CUDASubprogramAttrs::Host) { 256f674ddc1SPeter Klausler Check(body); 257f674ddc1SPeter Klausler } 258f674ddc1SPeter Klausler } 259f674ddc1SPeter Klausler } 260f674ddc1SPeter Klausler void Check(const parser::Block &block) { 261f674ddc1SPeter Klausler for (const auto &epc : block) { 262f674ddc1SPeter Klausler Check(epc); 263f674ddc1SPeter Klausler } 264f674ddc1SPeter Klausler } 265f674ddc1SPeter Klausler 266f674ddc1SPeter Klausler private: 267f674ddc1SPeter Klausler void Check(const parser::ExecutionPartConstruct &epc) { 268f674ddc1SPeter Klausler common::visit( 269f674ddc1SPeter Klausler common::visitors{ 270f674ddc1SPeter Klausler [&](const parser::ExecutableConstruct &x) { Check(x); }, 271f674ddc1SPeter Klausler [&](const parser::Statement<common::Indirection<parser::EntryStmt>> 272f674ddc1SPeter Klausler &x) { 273f674ddc1SPeter Klausler context_.Say(x.source, 274f674ddc1SPeter Klausler "Device code may not contain an ENTRY statement"_err_en_US); 275f674ddc1SPeter Klausler }, 276f674ddc1SPeter Klausler [](const parser::Statement<common::Indirection<parser::FormatStmt>> 277f674ddc1SPeter Klausler &) {}, 278f674ddc1SPeter Klausler [](const parser::Statement<common::Indirection<parser::DataStmt>> 279f674ddc1SPeter Klausler &) {}, 280f674ddc1SPeter Klausler [](const parser::Statement< 281f674ddc1SPeter Klausler common::Indirection<parser::NamelistStmt>> &) {}, 282f674ddc1SPeter Klausler [](const parser::ErrorRecovery &) {}, 283f674ddc1SPeter Klausler }, 284f674ddc1SPeter Klausler epc.u); 285f674ddc1SPeter Klausler } 286f674ddc1SPeter Klausler void Check(const parser::ExecutableConstruct &ec) { 287f674ddc1SPeter Klausler common::visit( 288f674ddc1SPeter Klausler common::visitors{ 289f674ddc1SPeter Klausler [&](const parser::Statement<parser::ActionStmt> &stmt) { 290f674ddc1SPeter Klausler Check(stmt.statement, stmt.source); 291f674ddc1SPeter Klausler }, 292f674ddc1SPeter Klausler [&](const common::Indirection<parser::DoConstruct> &x) { 293f674ddc1SPeter Klausler if (const std::optional<parser::LoopControl> &control{ 294f674ddc1SPeter Klausler x.value().GetLoopControl()}) { 295f674ddc1SPeter Klausler common::visit([&](const auto &y) { Check(y); }, control->u); 296f674ddc1SPeter Klausler } 297f674ddc1SPeter Klausler Check(std::get<parser::Block>(x.value().t)); 298f674ddc1SPeter Klausler }, 299f674ddc1SPeter Klausler [&](const common::Indirection<parser::BlockConstruct> &x) { 300f674ddc1SPeter Klausler Check(std::get<parser::Block>(x.value().t)); 301f674ddc1SPeter Klausler }, 302f674ddc1SPeter Klausler [&](const common::Indirection<parser::IfConstruct> &x) { 303f674ddc1SPeter Klausler Check(x.value()); 304f674ddc1SPeter Klausler }, 305e3dafa88SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::CaseConstruct> &x) { 306e3dafa88SValentin Clement (バレンタイン クレメン) const auto &caseList{ 307e3dafa88SValentin Clement (バレンタイン クレメン) std::get<std::list<parser::CaseConstruct::Case>>( 308e3dafa88SValentin Clement (バレンタイン クレメン) x.value().t)}; 309e3dafa88SValentin Clement (バレンタイン クレメン) for (const parser::CaseConstruct::Case &c : caseList) { 310e3dafa88SValentin Clement (バレンタイン クレメン) Check(std::get<parser::Block>(c.t)); 311e3dafa88SValentin Clement (バレンタイン クレメン) } 312e3dafa88SValentin Clement (バレンタイン クレメン) }, 313f674ddc1SPeter Klausler [&](const auto &x) { 314f674ddc1SPeter Klausler if (auto source{parser::GetSource(x)}) { 315f674ddc1SPeter Klausler context_.Say(*source, 316f674ddc1SPeter Klausler "Statement may not appear in device code"_err_en_US); 317f674ddc1SPeter Klausler } 318f674ddc1SPeter Klausler }, 319f674ddc1SPeter Klausler }, 320f674ddc1SPeter Klausler ec.u); 321f674ddc1SPeter Klausler } 322896b5e55SValentin Clement (バレンタイン クレメン) template <typename SEEK, typename A> 323896b5e55SValentin Clement (バレンタイン クレメン) static const SEEK *GetIOControl(const A &stmt) { 324896b5e55SValentin Clement (バレンタイン クレメン) for (const auto &spec : stmt.controls) { 325896b5e55SValentin Clement (バレンタイン クレメン) if (const auto *result{std::get_if<SEEK>(&spec.u)}) { 326896b5e55SValentin Clement (バレンタイン クレメン) return result; 327896b5e55SValentin Clement (バレンタイン クレメン) } 328896b5e55SValentin Clement (バレンタイン クレメン) } 329896b5e55SValentin Clement (バレンタイン クレメン) return nullptr; 330896b5e55SValentin Clement (バレンタイン クレメン) } 331896b5e55SValentin Clement (バレンタイン クレメン) template <typename A> static bool IsInternalIO(const A &stmt) { 332896b5e55SValentin Clement (バレンタイン クレメン) if (stmt.iounit.has_value()) { 333896b5e55SValentin Clement (バレンタイン クレメン) return std::holds_alternative<Fortran::parser::Variable>(stmt.iounit->u); 334896b5e55SValentin Clement (バレンタイン クレメン) } 335896b5e55SValentin Clement (バレンタイン クレメン) if (auto *unit{GetIOControl<Fortran::parser::IoUnit>(stmt)}) { 336896b5e55SValentin Clement (バレンタイン クレメン) return std::holds_alternative<Fortran::parser::Variable>(unit->u); 337896b5e55SValentin Clement (バレンタイン クレメン) } 338896b5e55SValentin Clement (バレンタイン クレメン) return false; 339896b5e55SValentin Clement (バレンタイン クレメン) } 340896b5e55SValentin Clement (バレンタイン クレメン) void WarnOnIoStmt(const parser::CharBlock &source) { 3410f973ac7SPeter Klausler context_.Warn(common::UsageWarning::CUDAUsage, source, 3420f973ac7SPeter Klausler "I/O statement might not be supported on device"_warn_en_US); 343505f6da1SPeter Klausler } 344896b5e55SValentin Clement (バレンタイン クレメン) template <typename A> 345896b5e55SValentin Clement (バレンタイン クレメン) void WarnIfNotInternal(const A &stmt, const parser::CharBlock &source) { 346896b5e55SValentin Clement (バレンタイン クレメン) if (!IsInternalIO(stmt)) { 347896b5e55SValentin Clement (バレンタイン クレメン) WarnOnIoStmt(source); 348896b5e55SValentin Clement (バレンタイン クレメン) } 349896b5e55SValentin Clement (バレンタイン クレメン) } 35016975ad2SValentin Clement (バレンタイン クレメン) template <typename A> 35173216cd7SPeter Klausler void ErrorIfHostSymbol(const A &expr, parser::CharBlock source) { 35273216cd7SPeter Klausler if (const Symbol * hostArray{FindHostArray{}(expr)}) { 35316975ad2SValentin Clement (バレンタイン クレメン) context_.Say(source, 35467ae944bSValentin Clement (バレンタイン クレメン) "Host array '%s' cannot be present in device context"_err_en_US, 35573216cd7SPeter Klausler hostArray->name()); 35616975ad2SValentin Clement (バレンタイン クレメン) } 35716975ad2SValentin Clement (バレンタイン クレメン) } 358e3dafa88SValentin Clement (バレンタイン クレメン) void ErrorInCUFKernel(parser::CharBlock source) { 359e3dafa88SValentin Clement (バレンタイン クレメン) if (IsCUFKernelDo) { 360e3dafa88SValentin Clement (バレンタイン クレメン) context_.Say( 361e3dafa88SValentin Clement (バレンタイン クレメン) source, "Statement may not appear in cuf kernel code"_err_en_US); 362e3dafa88SValentin Clement (バレンタイン クレメン) } 363e3dafa88SValentin Clement (バレンタイン クレメン) } 364f674ddc1SPeter Klausler void Check(const parser::ActionStmt &stmt, const parser::CharBlock &source) { 365f674ddc1SPeter Klausler common::visit( 366f674ddc1SPeter Klausler common::visitors{ 367e3dafa88SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::CycleStmt> &) { 368e3dafa88SValentin Clement (バレンタイン クレメン) ErrorInCUFKernel(source); 369e3dafa88SValentin Clement (バレンタイン クレメン) }, 370e3dafa88SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::ExitStmt> &) { 371e3dafa88SValentin Clement (バレンタイン クレメン) ErrorInCUFKernel(source); 372e3dafa88SValentin Clement (バレンタイン クレメン) }, 373e3dafa88SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::GotoStmt> &) { 374e3dafa88SValentin Clement (バレンタイン クレメン) ErrorInCUFKernel(source); 375e3dafa88SValentin Clement (バレンタイン クレメン) }, 3767009b069SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::StopStmt> &) { return; }, 377896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::PrintStmt> &) {}, 378896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::WriteStmt> &x) { 379896b5e55SValentin Clement (バレンタイン クレメン) if (x.value().format) { // Formatted write to '*' or '6' 380896b5e55SValentin Clement (バレンタイン クレメン) if (std::holds_alternative<Fortran::parser::Star>( 381896b5e55SValentin Clement (バレンタイン クレメン) x.value().format->u)) { 382896b5e55SValentin Clement (バレンタイン クレメン) if (x.value().iounit) { 383896b5e55SValentin Clement (バレンタイン クレメン) if (std::holds_alternative<Fortran::parser::Star>( 384896b5e55SValentin Clement (バレンタイン クレメン) x.value().iounit->u)) { 385896b5e55SValentin Clement (バレンタイン クレメン) return; 386896b5e55SValentin Clement (バレンタイン クレメン) } 387896b5e55SValentin Clement (バレンタイン クレメン) } 388896b5e55SValentin Clement (バレンタイン クレメン) } 389896b5e55SValentin Clement (バレンタイン クレメン) } 390896b5e55SValentin Clement (バレンタイン クレメン) WarnIfNotInternal(x.value(), source); 391896b5e55SValentin Clement (バレンタイン クレメン) }, 392896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::CloseStmt> &x) { 393896b5e55SValentin Clement (バレンタイン クレメン) WarnOnIoStmt(source); 394896b5e55SValentin Clement (バレンタイン クレメン) }, 395896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::EndfileStmt> &x) { 396896b5e55SValentin Clement (バレンタイン クレメン) WarnOnIoStmt(source); 397896b5e55SValentin Clement (バレンタイン クレメン) }, 398896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::OpenStmt> &x) { 399896b5e55SValentin Clement (バレンタイン クレメン) WarnOnIoStmt(source); 400896b5e55SValentin Clement (バレンタイン クレメン) }, 401896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::ReadStmt> &x) { 402896b5e55SValentin Clement (バレンタイン クレメン) WarnIfNotInternal(x.value(), source); 403896b5e55SValentin Clement (バレンタイン クレメン) }, 404896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::InquireStmt> &x) { 405896b5e55SValentin Clement (バレンタイン クレメン) WarnOnIoStmt(source); 406896b5e55SValentin Clement (バレンタイン クレメン) }, 407896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::RewindStmt> &x) { 408896b5e55SValentin Clement (バレンタイン クレメン) WarnOnIoStmt(source); 409896b5e55SValentin Clement (バレンタイン クレメン) }, 410896b5e55SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::BackspaceStmt> &x) { 411896b5e55SValentin Clement (バレンタイン クレメン) WarnOnIoStmt(source); 412896b5e55SValentin Clement (バレンタイン クレメン) }, 413a309c07aSValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::IfStmt> &x) { 414a309c07aSValentin Clement (バレンタイン クレメン) Check(x.value()); 415a309c07aSValentin Clement (バレンタイン クレメン) }, 41616975ad2SValentin Clement (バレンタイン クレメン) [&](const common::Indirection<parser::AssignmentStmt> &x) { 41767ae944bSValentin Clement (バレンタイン クレメン) if (const evaluate::Assignment * 41867ae944bSValentin Clement (バレンタイン クレメン) assign{semantics::GetAssignment(x.value())}) { 41916975ad2SValentin Clement (バレンタイン クレメン) ErrorIfHostSymbol(assign->lhs, source); 42016975ad2SValentin Clement (バレンタイン クレメン) ErrorIfHostSymbol(assign->rhs, source); 42116975ad2SValentin Clement (バレンタイン クレメン) } 42216975ad2SValentin Clement (バレンタイン クレメン) if (auto msg{ActionStmtChecker<IsCUFKernelDo>::WhyNotOk(x)}) { 42316975ad2SValentin Clement (バレンタイン クレメン) context_.Say(source, std::move(*msg)); 42416975ad2SValentin Clement (バレンタイン クレメン) } 42516975ad2SValentin Clement (バレンタイン クレメン) }, 426f674ddc1SPeter Klausler [&](const auto &x) { 427f674ddc1SPeter Klausler if (auto msg{ActionStmtChecker<IsCUFKernelDo>::WhyNotOk(x)}) { 428f674ddc1SPeter Klausler context_.Say(source, std::move(*msg)); 429f674ddc1SPeter Klausler } 430f674ddc1SPeter Klausler }, 431f674ddc1SPeter Klausler }, 432f674ddc1SPeter Klausler stmt.u); 433f674ddc1SPeter Klausler } 434f674ddc1SPeter Klausler void Check(const parser::IfConstruct &ic) { 435f674ddc1SPeter Klausler const auto &ifS{std::get<parser::Statement<parser::IfThenStmt>>(ic.t)}; 436f674ddc1SPeter Klausler CheckUnwrappedExpr(context_, ifS.source, 437f674ddc1SPeter Klausler std::get<parser::ScalarLogicalExpr>(ifS.statement.t)); 438f674ddc1SPeter Klausler Check(std::get<parser::Block>(ic.t)); 439f674ddc1SPeter Klausler for (const auto &eib : 440f674ddc1SPeter Klausler std::get<std::list<parser::IfConstruct::ElseIfBlock>>(ic.t)) { 441f674ddc1SPeter Klausler const auto &eIfS{std::get<parser::Statement<parser::ElseIfStmt>>(eib.t)}; 442f674ddc1SPeter Klausler CheckUnwrappedExpr(context_, eIfS.source, 443f674ddc1SPeter Klausler std::get<parser::ScalarLogicalExpr>(eIfS.statement.t)); 444f674ddc1SPeter Klausler Check(std::get<parser::Block>(eib.t)); 445f674ddc1SPeter Klausler } 446f674ddc1SPeter Klausler if (const auto &eb{ 447f674ddc1SPeter Klausler std::get<std::optional<parser::IfConstruct::ElseBlock>>(ic.t)}) { 448f674ddc1SPeter Klausler Check(std::get<parser::Block>(eb->t)); 449f674ddc1SPeter Klausler } 450f674ddc1SPeter Klausler } 451a309c07aSValentin Clement (バレンタイン クレメン) void Check(const parser::IfStmt &is) { 452a309c07aSValentin Clement (バレンタイン クレメン) const auto &uS{ 453a309c07aSValentin Clement (バレンタイン クレメン) std::get<parser::UnlabeledStatement<parser::ActionStmt>>(is.t)}; 454a309c07aSValentin Clement (バレンタイン クレメン) CheckUnwrappedExpr( 455a309c07aSValentin Clement (バレンタイン クレメン) context_, uS.source, std::get<parser::ScalarLogicalExpr>(is.t)); 456a309c07aSValentin Clement (バレンタイン クレメン) Check(uS.statement, uS.source); 457a309c07aSValentin Clement (バレンタイン クレメン) } 458f674ddc1SPeter Klausler void Check(const parser::LoopControl::Bounds &bounds) { 459f674ddc1SPeter Klausler Check(bounds.lower); 460f674ddc1SPeter Klausler Check(bounds.upper); 461f674ddc1SPeter Klausler if (bounds.step) { 462f674ddc1SPeter Klausler Check(*bounds.step); 463f674ddc1SPeter Klausler } 464f674ddc1SPeter Klausler } 465f674ddc1SPeter Klausler void Check(const parser::LoopControl::Concurrent &x) { 466f674ddc1SPeter Klausler const auto &header{std::get<parser::ConcurrentHeader>(x.t)}; 467f674ddc1SPeter Klausler for (const auto &cc : 468f674ddc1SPeter Klausler std::get<std::list<parser::ConcurrentControl>>(header.t)) { 469f674ddc1SPeter Klausler Check(std::get<1>(cc.t)); 470f674ddc1SPeter Klausler Check(std::get<2>(cc.t)); 471f674ddc1SPeter Klausler if (const auto &step{ 472f674ddc1SPeter Klausler std::get<std::optional<parser::ScalarIntExpr>>(cc.t)}) { 473f674ddc1SPeter Klausler Check(*step); 474f674ddc1SPeter Klausler } 475f674ddc1SPeter Klausler } 476f674ddc1SPeter Klausler if (const auto &mask{ 477f674ddc1SPeter Klausler std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) { 478f674ddc1SPeter Klausler Check(*mask); 479f674ddc1SPeter Klausler } 480f674ddc1SPeter Klausler } 481f674ddc1SPeter Klausler void Check(const parser::ScalarLogicalExpr &x) { 482f674ddc1SPeter Klausler Check(DEREF(parser::Unwrap<parser::Expr>(x))); 483f674ddc1SPeter Klausler } 484f674ddc1SPeter Klausler void Check(const parser::ScalarIntExpr &x) { 485f674ddc1SPeter Klausler Check(DEREF(parser::Unwrap<parser::Expr>(x))); 486f674ddc1SPeter Klausler } 487f674ddc1SPeter Klausler void Check(const parser::ScalarExpr &x) { 488f674ddc1SPeter Klausler Check(DEREF(parser::Unwrap<parser::Expr>(x))); 489f674ddc1SPeter Klausler } 490f674ddc1SPeter Klausler void Check(const parser::Expr &expr) { 491f674ddc1SPeter Klausler if (MaybeMsg msg{DeviceExprChecker{}(expr.typedExpr)}) { 492f674ddc1SPeter Klausler context_.Say(expr.source, std::move(*msg)); 493f674ddc1SPeter Klausler } 494f674ddc1SPeter Klausler } 495f674ddc1SPeter Klausler 496f674ddc1SPeter Klausler SemanticsContext &context_; 497f674ddc1SPeter Klausler }; 498f674ddc1SPeter Klausler 499f674ddc1SPeter Klausler void CUDAChecker::Enter(const parser::SubroutineSubprogram &x) { 500f674ddc1SPeter Klausler DeviceContextChecker<false>{context_}.CheckSubprogram( 501f674ddc1SPeter Klausler std::get<parser::Name>( 502f674ddc1SPeter Klausler std::get<parser::Statement<parser::SubroutineStmt>>(x.t).statement.t), 503f674ddc1SPeter Klausler std::get<parser::ExecutionPart>(x.t).v); 504f674ddc1SPeter Klausler } 505f674ddc1SPeter Klausler 506f674ddc1SPeter Klausler void CUDAChecker::Enter(const parser::FunctionSubprogram &x) { 507f674ddc1SPeter Klausler DeviceContextChecker<false>{context_}.CheckSubprogram( 508f674ddc1SPeter Klausler std::get<parser::Name>( 509f674ddc1SPeter Klausler std::get<parser::Statement<parser::FunctionStmt>>(x.t).statement.t), 510f674ddc1SPeter Klausler std::get<parser::ExecutionPart>(x.t).v); 511f674ddc1SPeter Klausler } 512f674ddc1SPeter Klausler 513f674ddc1SPeter Klausler void CUDAChecker::Enter(const parser::SeparateModuleSubprogram &x) { 514f674ddc1SPeter Klausler DeviceContextChecker<false>{context_}.CheckSubprogram( 515f674ddc1SPeter Klausler std::get<parser::Statement<parser::MpSubprogramStmt>>(x.t).statement.v, 516f674ddc1SPeter Klausler std::get<parser::ExecutionPart>(x.t).v); 517f674ddc1SPeter Klausler } 518f674ddc1SPeter Klausler 519f674ddc1SPeter Klausler // !$CUF KERNEL DO semantic checks 520f674ddc1SPeter Klausler 521f674ddc1SPeter Klausler static int DoConstructTightNesting( 522f674ddc1SPeter Klausler const parser::DoConstruct *doConstruct, const parser::Block *&innerBlock) { 523*3d59e30cSValentin Clement (バレンタイン クレメン) if (!doConstruct || 524*3d59e30cSValentin Clement (バレンタイン クレメン) (!doConstruct->IsDoNormal() && !doConstruct->IsDoConcurrent())) { 525f674ddc1SPeter Klausler return 0; 526f674ddc1SPeter Klausler } 527f674ddc1SPeter Klausler innerBlock = &std::get<parser::Block>(doConstruct->t); 528f674ddc1SPeter Klausler if (innerBlock->size() == 1) { 529f674ddc1SPeter Klausler if (const auto *execConstruct{ 530f674ddc1SPeter Klausler std::get_if<parser::ExecutableConstruct>(&innerBlock->front().u)}) { 531f674ddc1SPeter Klausler if (const auto *next{ 532f674ddc1SPeter Klausler std::get_if<common::Indirection<parser::DoConstruct>>( 533f674ddc1SPeter Klausler &execConstruct->u)}) { 534f674ddc1SPeter Klausler return 1 + DoConstructTightNesting(&next->value(), innerBlock); 535f674ddc1SPeter Klausler } 536f674ddc1SPeter Klausler } 537f674ddc1SPeter Klausler } 538f674ddc1SPeter Klausler return 1; 539f674ddc1SPeter Klausler } 540f674ddc1SPeter Klausler 5415bbb63bdSPeter Klausler static void CheckReduce( 5425bbb63bdSPeter Klausler SemanticsContext &context, const parser::CUFReduction &reduce) { 5435bbb63bdSPeter Klausler auto op{std::get<parser::CUFReduction::Operator>(reduce.t).v}; 5445bbb63bdSPeter Klausler for (const auto &var : 5455bbb63bdSPeter Klausler std::get<std::list<parser::Scalar<parser::Variable>>>(reduce.t)) { 5465bbb63bdSPeter Klausler if (const auto &typedExprPtr{var.thing.typedExpr}; 5475bbb63bdSPeter Klausler typedExprPtr && typedExprPtr->v) { 5485bbb63bdSPeter Klausler const auto &expr{*typedExprPtr->v}; 5495bbb63bdSPeter Klausler if (auto type{expr.GetType()}) { 5505bbb63bdSPeter Klausler auto cat{type->category()}; 5515bbb63bdSPeter Klausler bool isOk{false}; 5525bbb63bdSPeter Klausler switch (op) { 5533af717d6Skhaki3 case parser::ReductionOperator::Operator::Plus: 5543af717d6Skhaki3 case parser::ReductionOperator::Operator::Multiply: 5553af717d6Skhaki3 case parser::ReductionOperator::Operator::Max: 5563af717d6Skhaki3 case parser::ReductionOperator::Operator::Min: 5574065d985SValentin Clement (バレンタイン クレメン) isOk = cat == TypeCategory::Integer || cat == TypeCategory::Real || 5584065d985SValentin Clement (バレンタイン クレメン) cat == TypeCategory::Complex; 5595bbb63bdSPeter Klausler break; 5603af717d6Skhaki3 case parser::ReductionOperator::Operator::Iand: 5613af717d6Skhaki3 case parser::ReductionOperator::Operator::Ior: 5623af717d6Skhaki3 case parser::ReductionOperator::Operator::Ieor: 5635bbb63bdSPeter Klausler isOk = cat == TypeCategory::Integer; 5645bbb63bdSPeter Klausler break; 5653af717d6Skhaki3 case parser::ReductionOperator::Operator::And: 5663af717d6Skhaki3 case parser::ReductionOperator::Operator::Or: 5673af717d6Skhaki3 case parser::ReductionOperator::Operator::Eqv: 5683af717d6Skhaki3 case parser::ReductionOperator::Operator::Neqv: 5695bbb63bdSPeter Klausler isOk = cat == TypeCategory::Logical; 5705bbb63bdSPeter Klausler break; 5715bbb63bdSPeter Klausler } 5725bbb63bdSPeter Klausler if (!isOk) { 5735bbb63bdSPeter Klausler context.Say(var.thing.GetSource(), 5745bbb63bdSPeter Klausler "!$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type %s"_err_en_US, 5755bbb63bdSPeter Klausler type->AsFortran()); 5765bbb63bdSPeter Klausler } 5775bbb63bdSPeter Klausler } 5785bbb63bdSPeter Klausler } 5795bbb63bdSPeter Klausler } 5805bbb63bdSPeter Klausler } 5815bbb63bdSPeter Klausler 582f674ddc1SPeter Klausler void CUDAChecker::Enter(const parser::CUFKernelDoConstruct &x) { 583f674ddc1SPeter Klausler auto source{std::get<parser::CUFKernelDoConstruct::Directive>(x.t).source}; 584f674ddc1SPeter Klausler const auto &directive{std::get<parser::CUFKernelDoConstruct::Directive>(x.t)}; 585f674ddc1SPeter Klausler std::int64_t depth{1}; 586f674ddc1SPeter Klausler if (auto expr{AnalyzeExpr(context_, 587f674ddc1SPeter Klausler std::get<std::optional<parser::ScalarIntConstantExpr>>( 588f674ddc1SPeter Klausler directive.t))}) { 589f674ddc1SPeter Klausler depth = evaluate::ToInt64(expr).value_or(0); 590f674ddc1SPeter Klausler if (depth <= 0) { 591f674ddc1SPeter Klausler context_.Say(source, 592f674ddc1SPeter Klausler "!$CUF KERNEL DO (%jd): loop nesting depth must be positive"_err_en_US, 593f674ddc1SPeter Klausler std::intmax_t{depth}); 594f674ddc1SPeter Klausler depth = 1; 595f674ddc1SPeter Klausler } 596f674ddc1SPeter Klausler } 597f674ddc1SPeter Klausler const parser::DoConstruct *doConstruct{common::GetPtrFromOptional( 598f674ddc1SPeter Klausler std::get<std::optional<parser::DoConstruct>>(x.t))}; 599f674ddc1SPeter Klausler const parser::Block *innerBlock{nullptr}; 600f674ddc1SPeter Klausler if (DoConstructTightNesting(doConstruct, innerBlock) < depth) { 601f674ddc1SPeter Klausler context_.Say(source, 602f674ddc1SPeter Klausler "!$CUF KERNEL DO (%jd) must be followed by a DO construct with tightly nested outer levels of counted DO loops"_err_en_US, 603f674ddc1SPeter Klausler std::intmax_t{depth}); 604f674ddc1SPeter Klausler } 605f674ddc1SPeter Klausler if (innerBlock) { 606f674ddc1SPeter Klausler DeviceContextChecker<true>{context_}.Check(*innerBlock); 607f674ddc1SPeter Klausler } 6085bbb63bdSPeter Klausler for (const auto &reduce : 6095bbb63bdSPeter Klausler std::get<std::list<parser::CUFReduction>>(directive.t)) { 6105bbb63bdSPeter Klausler CheckReduce(context_, reduce); 6115bbb63bdSPeter Klausler } 612cfc09511SValentin Clement (バレンタイン クレメン) inCUFKernelDoConstruct_ = true; 613cfc09511SValentin Clement (バレンタイン クレメン) } 614cfc09511SValentin Clement (バレンタイン クレメン) 615cfc09511SValentin Clement (バレンタイン クレメン) void CUDAChecker::Leave(const parser::CUFKernelDoConstruct &) { 616cfc09511SValentin Clement (バレンタイン クレメン) inCUFKernelDoConstruct_ = false; 617f674ddc1SPeter Klausler } 618f674ddc1SPeter Klausler 6190aa982fbSValentin Clement (バレンタイン クレメン) void CUDAChecker::Enter(const parser::AssignmentStmt &x) { 6207b6b0231SValentin Clement (バレンタイン クレメン) auto lhsLoc{std::get<parser::Variable>(x.t).GetSource()}; 6217b6b0231SValentin Clement (バレンタイン クレメン) const auto &scope{context_.FindScope(lhsLoc)}; 6227b6b0231SValentin Clement (バレンタイン クレメン) const Scope &progUnit{GetProgramUnitContaining(scope)}; 623cfc09511SValentin Clement (バレンタイン クレメン) if (IsCUDADeviceContext(&progUnit) || inCUFKernelDoConstruct_) { 6247b6b0231SValentin Clement (バレンタイン クレメン) return; // Data transfer with assignment is only perform on host. 6257b6b0231SValentin Clement (バレンタイン クレメン) } 6267b6b0231SValentin Clement (バレンタイン クレメン) 6270aa982fbSValentin Clement (バレンタイン クレメン) const evaluate::Assignment *assign{semantics::GetAssignment(x)}; 62826101e8cSValentin Clement (バレンタイン クレメン) if (!assign) { 62926101e8cSValentin Clement (バレンタイン クレメン) return; 63026101e8cSValentin Clement (バレンタイン クレメン) } 63126101e8cSValentin Clement (バレンタイン クレメン) 6328e8dccdeSValentin Clement (バレンタイン クレメン) int nbLhs{evaluate::GetNbOfCUDADeviceSymbols(assign->lhs)}; 6338e8dccdeSValentin Clement (バレンタイン クレメン) int nbRhs{evaluate::GetNbOfCUDADeviceSymbols(assign->rhs)}; 6340aa982fbSValentin Clement (バレンタイン クレメン) 6350aa982fbSValentin Clement (バレンタイン クレメン) // device to host transfer with more than one device object on the rhs is not 6360aa982fbSValentin Clement (バレンタイン クレメン) // legal. 6370aa982fbSValentin Clement (バレンタイン クレメン) if (nbLhs == 0 && nbRhs > 1) { 6380aa982fbSValentin Clement (バレンタイン クレメン) context_.Say(lhsLoc, 6390aa982fbSValentin Clement (バレンタイン クレメン) "More than one reference to a CUDA object on the right hand side of the assigment"_err_en_US); 6400aa982fbSValentin Clement (バレンタイン クレメン) } 6410aa982fbSValentin Clement (バレンタイン クレメン) } 6420aa982fbSValentin Clement (バレンタイン クレメン) 643f674ddc1SPeter Klausler } // namespace Fortran::semantics 644